Las variables en Python son solo punteros, hasta donde yo sé.
Según esta regla, puedo asumir que el resultado de este fragmento de código:
i = 5
j = i
j = 3
print(i)
sería 3
. Pero obtuve un resultado inesperado para mí, lo fue 5
.
Además, mi libro de Python cubre este ejemplo:
i = [1,2,3]
j = i
i[0] = 5
print(j)
el resultado sería [5,2,3]
.
¿Qué estoy entendiendo mal?
i
debería haber sido igual a3
Respuestas:
Los llamamos referencias. Ellos funcionan asi
i = 5 # create int(5) instance, bind it to i j = i # bind j to the same int as i j = 3 # create int(3) instance, bind it to j print i # i still bound to the int(5), j bound to the int(3)
Los pequeños entres están internados, pero eso no es importante para esta explicación.
i = [1,2,3] # create the list instance, and bind it to i j = i # bind j to the same list as i i[0] = 5 # change the first item of i print j # j is still bound to the same list as i
fuente
40
, hace referencia al mismo objeto en la memoria. Para ver este tipo a, b = 256 y pruebaa is b
. Ahora inténtelo con a, b = 257. Ver: stackoverflow.com/a/1136852/3973834 y codementor.io/python/tutorial/…Las variables no son indicadores. Cuando asigna a una variable, está vinculando el nombre a un objeto. A partir de ese momento, puede hacer referencia al objeto utilizando el nombre, hasta que ese nombre se recupere.
En su primer ejemplo, el nombre
i
está vinculado al valor5
. La vinculación de diferentes valores al nombrej
no tiene ningún efectoi
, por lo que cuando imprime más tarde, el valor deli
valor sigue siendo5
.En su segundo ejemplo, vincula ambos
i
yj
al mismo objeto de lista. Cuando modifica el contenido de la lista, puede ver el cambio independientemente del nombre que utilice para hacer referencia a la lista.Tenga en cuenta que sería incorrecto decir "ambas listas han cambiado". Solo hay una lista pero tiene dos nombres (
i
yj
) que se refieren a ella.Documentación relacionada
fuente
Las variables de Python son nombres vinculados a objetos
De los documentos :
Cuando tu lo hagas
i = 5 j = i
eso es lo mismo que hacer:
i = 5 j = 5
j
no señalai
, y después de la tarea,j
no sabe quei
existe.j
está simplemente vinculado a lo que sea que estéi
apuntando en el momento de la asignación.Si hiciera las asignaciones en la misma línea, se vería así:
i = j = 5
Y el resultado sería exactamente el mismo.
Así, más tarde haciendo
i = 3
no cambia lo que
j
está apuntando, y puede intercambiarlo,j = 3
no cambiaría lo quei
está apuntando.Tu ejemplo no elimina la referencia a la lista
Entonces, cuando haces esto:
i = [1,2,3] j = i
Es lo mismo que hacer esto:
i = j = [1,2,3]
entonces
i
yj
ambos apuntan a la misma lista. Entonces tu ejemplo muta la lista:i[0] = 5
Las listas de Python son objetos mutables, por lo que cuando cambia la lista de una referencia y la mira desde otra referencia, verá el mismo resultado porque es la misma lista.
fuente
TLDR: los nombres de Python funcionan como punteros con des / referenciación automática pero no permiten operaciones de puntero explícitas. Otros objetivos representan indirecciones, que se comportan de manera similar a los punteros.
La implementación de CPython usa punteros de tipo
PyObject*
bajo el capó. Como tal, es posible traducir la semántica de nombres a operaciones de puntero. La clave es separar los nombres de los objetos reales .El código de Python de ejemplo incluye tanto nombres (
i
) como objetos (5
).i = 5 # name `i` refers to object `5` j = i # ??? j = 3 # name `j` refers to object `3`
Esto se puede traducir aproximadamente a código C con nombres y objetos separados .
int three=3, five=5; // objects int *i, *j; // names i = &five; // name `i` refers to position of object `5` j = i; // name `j` refers to referent of `i` j = &three; // name `j` refers to position of object `3`
¡La parte importante es que los "nombres como punteros" no almacenan objetos! No definimos
*i = five
, peroi = &five
. Los nombres y los objetos existen independientemente unos de otros.Los nombres solo apuntan a objetos existentes en la memoria.
Al asignar un nombre a otro, no se intercambian objetos. Cuando definimos
j = i
, esto es equivalente aj = &five
. Ningunoi
nij
está conectado con el otro.+- name i -+ -\ \ --> + <five> -+ / | 5 | +- name j -+ -/ +----------+
Como resultado, cambiar el destino de un nombre no afecta al otro . Solo actualiza lo que apunta ese nombre específico.
Python también tiene otros tipos de elementos similares a nombres : referencias de atributos (
i.j
), suscripciones (i[j]
) y segmentaciones (i[:j]
). A diferencia de los nombres, que se refieren directamente a objetos, los tres se refieren indirectamente a elementos de objetos.El código de ejemplo incluye nombres (
i
) y una suscripción (i[0]
).i = [1,2,3] # name `i` refers to object `[1, 2, 3]` j = i # name `j` refers to referent of `i` i[0] = 5 # ???
Un CPython
list
usa una matriz C dePyObject*
punteros debajo del capó. De nuevo, esto se puede traducir aproximadamente a código C con nombres y objetos separados.typedef struct{ int *elements[3]; } list; // length 3 `list` type int one = 1, two = 2, three = 3, five = 5; list values = {&one, &two, &three}; // objects list *i, *j; // names i = &values; // name `i` refers to object `[1, 2, 3]` j = i; // name `j` refers to referent of `i` i->elements[0] = &five; // leading element of `i` refers to object `5`
¡Lo importante es que no cambiamos ningún nombre! Cambiamos
i->elements[0]
, el elemento de un objeto al que apuntan nuestros nombres.Se pueden cambiar los valores de los objetos compuestos existentes.
Cuando se cambia el valor de un objeto mediante un nombre, los nombres no se cambian. Ambos
i
yj
todavía se refieren al mismo objeto, cuyo valor podemos cambiar.+- name i -+ -\ \ --> + <values> -+ / | elements | --> [1, 2, 3] +- name j -+ -/ +-----------+
El objeto intermedio se comporta de manera similar a un puntero en el sentido de que podemos cambiar directamente a qué apunta y hacer referencia a él desde varios nombres.
fuente
i
yj
en su ejemplo. Se empieza coni = 5
,j = 3
y luego invertirlos en el resto de su puesto. Dicho esto de nuevo, esta es la única respuesta en mi opinión que hace justicia a la pregunta en el OP y realmente explica lo que sucede bajo el capó.No son del todo apuntadores, son referencias a objetos. Los objetos pueden ser mutables o inmutables. Un objeto inmutable se copia cuando se modifica. Un objeto mutable se modifica in situ. Un número entero es un objeto inmutable, al que hace referencia mediante sus variables i y j. Una lista es un objeto mutable.
En tu primer ejemplo
i=5 # The label i now references 5 j=i # The label j now references what i references j=3 # The label j now references 3 print i # i still references 5
En su segundo ejemplo:
i=[1,2,3] # i references a list object (a mutable object) j=i # j now references the same object as i (they reference the same mutable object) i[0]=5 # sets first element of references object to 5 print j # prints the list object that j references. It's the same one as i.
fuente
Cuando establece que
j=3
la etiquetaj
ya no se aplica (señala) ai
, comienza a apuntar al número entero3
. El nombrei
todavía se refiere al valor que estableció originalmente5
,.fuente
cualquier variable que esté en el lado izquierdo del signo '=' se asigna con el valor en el lado derecho de '='
i = 5
j = i
--- j tiene 5j = 3
--- j tiene 3 (sobrescribe el valor de 5) pero no se ha cambiado nada con respecto a iprint(i)
- entonces esto imprime 5fuente
La asignación no modifica los objetos; todo lo que hace es cambiar donde apunta la variable. Cambiar dónde apunta una variable no cambiará dónde apunta otra.
Probablemente esté pensando en el hecho de que las listas y los diccionarios son tipos mutables. Hay operadores para modificar los objetos reales en el lugar, y si usa uno de ellos, verá el cambio en todas las variables que apuntan al mismo objeto:
x = [] y = x x.append(1) # x and y both are now [1]
Pero la asignación aún mueve el puntero:
x = [2] # x now points to new list [2]; y still points to old list [1]
Los números, a diferencia de los diccionarios y las listas, son inmutables. Si lo hace
x = 3; x += 2
, no está transformando el número 3 en el número 5; en su lugar, solo está haciendo que la variablex
apunte a 5. El 3 sigue ahí sin cambios, y cualquier variable que apunte a él seguirá viendo 3 como su valor.(En la implementación real, los números probablemente no sean tipos de referencia en absoluto; es más probable que las variables realmente contengan una representación del valor directamente en lugar de apuntar a él. Pero ese detalle de implementación no cambia la semántica en lo que respecta a los tipos inmutables .)
fuente
En Python, todo es objeto, incluidas las piezas de memoria que le devuelven. Eso significa que, cuando se crea un nuevo fragmento de memoria (independientemente de lo que haya creado: int, str, objeto personalizado, etc.), tiene un nuevo objeto de memoria. En su caso, esta es la asignación a 3 que crea un nuevo objeto (memoria) y, por lo tanto, tiene una nueva dirección.
Si ejecuta lo siguiente, verá lo que quiero decir fácilmente.
i = 5 j = i print("id of j: {}", id(j)) j = 3 print("id of j: {}", id(j))
En mi opinión, en cuanto a la memoria, esta es la comprensión / diferencia clave entre C y Python. En C / C ++, se le devuelve un puntero de memoria (si usa sintaxis de puntero, por supuesto) en lugar de un objeto de memoria que le brinda más flexibilidad en términos de cambiar la dirección referida.
fuente