¿Python asigna múltiples variables al mismo valor? comportamiento de la lista

131

Traté de usar la asignación múltiple como se muestra a continuación para inicializar las variables, pero el comportamiento me confundió, espero reasignar la lista de valores por separado, quiero decir que b [0] yc [0] son ​​0 como antes.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

El resultado es: [1, 3, 5] [1, 3, 5] [1, 3, 5]

¿Es eso correcto? ¿Qué debo usar para la asignación múltiple? ¿Qué es diferente de esto?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

resultado: ('f:', 3) ('e:', 4)

Marco
fuente
2
¿Quieres a, by c,a todos los puntos con el mismo valor (en este caso una lista), o quiere a=0, b=3y c=5. En ese caso, quieres a,b,c = [0,3,5]o simplemente a,b,c = 0,3,5.
chepner

Respuestas:

271

Si vienes a Python desde un lenguaje en C / Java / etc. familia, puede ayudarlo a dejar de pensar en auna "variable" y comenzar a pensar en ella como un "nombre".

a, by cno son variables diferentes con valores iguales; son nombres diferentes para el mismo valor idéntico. Las variables tienen tipos, identidades, direcciones y todo tipo de cosas como esa.

Los nombres no tienen nada de eso. Los valores sí, por supuesto, y puede tener muchos nombres para el mismo valor.

Si le das Notorious B.I.G.un hot dog, * Biggie Smallsy Chris Wallacetienes un hot dog. Si cambia el primer elemento de aa 1, los primeros elementos de by cson 1.

Si desea saber si dos nombres están nombrando el mismo objeto, use el isoperador:

>>> a=b=c=[0,3,5]
>>> a is b
True

Luego preguntas:

¿Qué es diferente de esto?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Aquí, vuelve a vincular el nombre eal valor 4. Eso no afecta los nombres dy fde ninguna manera.

En su versión anterior, estaba asignando a a[0], no a a. Entonces, desde el punto de vista de a[0], estás volviendo a vincular a[0], pero desde el punto de vista de a, lo estás cambiando en el lugar.

Puede usar la idfunción, que le proporciona un número único que representa la identidad de un objeto, para ver exactamente qué objeto es cuál, incluso cuando isno puede ayudar:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Observe que a[0]ha cambiado de 4297261120 a 4297261216; ahora es un nombre para un valor diferente. Y b[0]ahora también es un nombre para ese mismo nuevo valor. Eso es porque ay btodavía están nombrando el mismo objeto.


Debajo de las cubiertas, en a[0]=1realidad está llamando a un método en el objeto de la lista. (Es equivalente a a.__setitem__(0, 1)). Entonces, en realidad no está volviendo a unir nada. Es como llamar my_object.set_something(1). Claro, es probable que el objeto vuelva a vincular un atributo de instancia para implementar este método, pero eso no es lo importante; lo importante es que no estás asignando nada, solo estás mutando el objeto. Y es lo mismo con a[0]=1.


user570826 preguntó:

¿Qué pasa si tenemos, a = b = c = 10

Esa es exactamente la misma situación que a = b = c = [1, 2, 3]: tiene tres nombres para el mismo valor.

Pero en este caso, el valor es an int, y ints son inmutables. En cualquiera de los casos, se puede volver a enlazar aa un valor diferente (por ejemplo, a = "Now I'm a string!"), pero el no va a afectar al valor original, el cual b, y cseguirá siendo nombres para. La diferencia es que con una lista, puede cambiar el valor [1, 2, 3]en [1, 2, 3, 4]la marcha, por ejemplo, a.append(4); ya que es en realidad el cambio del valor que by cson nombres para, bahora quieren b [1, 2, 3, 4]. No hay forma de cambiar el valor 10en otra cosa. 10tiene 10 años para siempre, al igual que Claudia el vampiro tiene 5 para siempre (al menos hasta que sea reemplazada por Kirsten Dunst).


* Advertencia: no le dé a Notorious BIG un hot dog. Los zombis del rap gangsta nunca deberían ser alimentados después de la medianoche.

abarnert
fuente
¿Qué have, a = b = c = 10;sucede si nosotros y cuando intentamos actualizar el valor de b, afecta a cualquier otro? aunque verifiqué que sus identificadores son los mismos.
AJ
@ user570826: 10es inmutable; eso significa que no hay forma de actualizar el valor, por lo que su pregunta no tiene sentido. Puede apuntar ba un valor diferente, pero hacerlo no tiene efecto en ay c, que todavía apuntan al valor original. La diferencia que marcan las listas es que son mutables, por ejemplo, puede appenden una lista, o lst[0] = 3, y eso actualizará el valor, que será visible a través de todos los nombres para ese valor.
abarnert
72

Tos tos

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 
Jimmy Kane
fuente
10
En mi humilde opinión, esto realmente responde a la primera pregunta clave de OP de qué debo usar para la asignación múltiple, mientras que la respuesta más alta y más cerebral anterior no.
Will Croxford
2
O a,b,c = 1,2,3sin corchetes funciona en Python 2 o 3, si realmente quieres ese cm extra de legibilidad.
Will Croxford
14

Sí, ese es el comportamiento esperado. a, byc se establecen como etiquetas para la misma lista. Si desea tres listas diferentes, debe asignarlas individualmente. Puede repetir la lista explícita o utilizar una de las numerosas formas de copiar una lista:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Las declaraciones de asignación en Python no copian objetos: vinculan el nombre a un objeto, y un objeto puede tener tantas etiquetas como usted establezca. En su primera edición, cambiando a [0], está actualizando un elemento de la lista única a la que se refieren a, byc. En su segundo, cambiando e, está cambiando e para que sea una etiqueta para un objeto diferente (4 en lugar de 3).

Peter DeGlopper
fuente
13

En python, todo es un objeto, también tipos de variables "simples" (int, float, etc.).

Cuando cambia un valor de variable, en realidad cambia su puntero , y si compara entre dos variables, compara sus punteros . (Para ser claros, el puntero es la dirección en la memoria física de la computadora donde se almacena una variable).

Como resultado, cuando cambia un valor de variable interna, cambia su valor en la memoria y afecta a todas las variables que apuntan a esta dirección.

Por ejemplo, cuando haces:

a = b =  5 

Esto significa que ayb apunta a la misma dirección en la memoria que contiene el valor 5, pero cuando lo hace:

a = 6

No afecta a b porque a ahora apunta a otra ubicación de memoria que contiene 6 yb todavía apunta a la dirección de memoria que contiene 5.

Pero cuando lo haces:

a = b = [1,2,3]

ayb, nuevamente, apunta a la misma ubicación pero la diferencia es que si cambia uno de los valores de la lista:

a[0] = 2

Cambia el valor de la memoria en la que a apunta, pero a todavía apunta a la misma dirección que b, y como resultado, b también cambia.

Ori Seri
fuente
66
Esto es muy engañoso. Los punteros ciertamente no son visibles en el nivel de Python, y al menos dos de las cuatro implementaciones principales (PyPy y Jython) no los usan ni siquiera dentro de la implementación.
abarnert
1
Le invitamos a leer y explorar aspectos internos de Python y descubrirá que cada variable en Python es en realidad puntero.
Ori Seri
44
No. En una implementación de Python (CPython), cada variable es un puntero a a PyObject. Eso no es cierto en otras implementaciones como PyPy o Jython. (De hecho, ni siquiera es claro cómo podría ser cierto, porque las lenguas esas implementaciones están escritas en ni siquiera tienen punteros.)
abarnert
Creo que el uso de "puntero" en un sentido conceptual está bien (quizás con un descargo de responsabilidad de que las implementaciones pueden variar), especialmente si el objetivo es transmitir el comportamiento.
Levon
@abarnert Cuando la gente dice Python se refieren a CPython, no a otras implementaciones raramente utilizadas. Al igual que cuando la gente dice Kleenex, se refieren al tejido facial. Jugar al juego semántico en estos comentarios es realmente innecesario. En cuanto a lo que escribió, ¿está mal el comportamiento de lo que describió?
swade
10

Puede usar id(name)para verificar si dos nombres representan el mismo objeto:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Las listas son mutables; significa que puede cambiar el valor en su lugar sin crear un nuevo objeto. Sin embargo, depende de cómo cambie el valor:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Si asigna una nueva lista para a, a continuación, su identificador va a cambiar, por lo que no afectará by c's valores:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Los enteros son inmutables, por lo que no puede cambiar el valor sin crear un nuevo objeto:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
jurgenreza
fuente
1
idNo es necesariamente una ubicación de memoria. Como dicen los documentos , esto devuelve la "identidad ... un número entero ... que se garantiza que es único y constante para este objeto durante su vida útil". CPython utiliza la dirección de memoria como id, pero otras implementaciones de Python pueden no hacerlo. PyPy, por ejemplo, no. Y decir "dos vars apuntan a la misma ubicación de memoria" es engañoso para cualquiera que lo entienda al estilo C. "Dos nombres para el mismo objeto" es más preciso y menos engañoso.
abarnert
@abarnert gracias por la aclaración, actualicé la respuesta.
jurgenreza
7

en tu primer ejemplo a = b = c = [1, 2, 3]realmente estás diciendo:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Si desea establecer 'a' igual a 1, 'b' igual a '2' y 'c' igual a 3, intente esto:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

¡Espero que esto ayude!

Nick Burns
fuente
4

En pocas palabras, en el primer caso, está asignando múltiples nombres a a list. Solo se crea una copia de la lista en la memoria y todos los nombres se refieren a esa ubicación. Por lo tanto, cambiar la lista usando cualquiera de los nombres en realidad modificará la lista en la memoria.

En el segundo caso, se crean múltiples copias del mismo valor en la memoria. Entonces cada copia es independiente la una de la otra.

Vikas
fuente
3

Lo que necesitas es esto:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
Pydsigner
fuente
3

El código que hace lo que necesito podría ser este:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Resultado:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Marco
fuente