Estoy tratando de entender el enfoque de Python para el alcance variable. En este ejemplo, ¿por qué es f()
capaz de alterar el valor de x
, como se percibe dentro main()
, pero no el valor de n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Salida:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Respuestas:
Algunas respuestas contienen la palabra "copiar" en el contexto de una llamada de función. Lo encuentro confuso.
Python no copia los objetos que pasas durante una llamada a la función cada vez .
Los parámetros de la función son nombres . Cuando llama a una función, Python vincula estos parámetros a cualquier objeto que pase (a través de nombres en un ámbito de llamada).
Los objetos pueden ser mutables (como listas) o inmutables (como enteros, cadenas en Python). Objeto mutable que puedes cambiar. No puede cambiar un nombre, solo puede vincularlo a otro objeto.
Su ejemplo no se trata de ámbitos o espacios de nombres , se trata de nombrar, enlazar y mutabilidad de un objeto en Python.
Aquí hay buenas fotos sobre la diferencia entre variables en otros idiomas y nombres en Python .
fuente
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
enf()
no tiene efecto en la listax
en la función principal. He actualizado el comentario, para hacerlo más específico.Ya tiene varias respuestas, y estoy ampliamente de acuerdo con JF Sebastian, pero puede encontrar esto útil como acceso directo:
Cada vez que veas
varname =
, estás creando un nuevo enlace de nombre dentro del alcance de la función. Cualquier valor al quevarname
se haya vinculado antes se pierde dentro de este alcance .Cada vez que ves
varname.foo()
estás llamando a un métodovarname
. El método puede alterar varname (por ejemplolist.append
).varname
(o, más bien, el objeto quevarname
nombra) puede existir en más de un ámbito, y dado que es el mismo objeto, cualquier cambio será visible en todos los ámbitos.[tenga en cuenta que la
global
palabra clave crea una excepción al primer caso]fuente
f
en realidad no altera el valor dex
(que siempre es la misma referencia a una instancia de una lista). Más bien, altera el contenido de esta lista.En ambos casos, se pasa una copia de una referencia a la función. Dentro de la función,
n
se le asigna un nuevo valor. Solo se modifica la referencia dentro de la función, no la que está fuera de ella.x
no se le asigna un nuevo valor: ni la referencia dentro ni fuera de la función se modifican. En cambio,x
el valor de se modifica.Dado que tanto el
x
interior de la función como el exterior se refieren al mismo valor, ambos ven la modificación. Por el contrario, eln
interior de la función y el exterior se refieren a diferentes valores después de quen
se reasignó dentro de la función.fuente
Cambiaré el nombre de las variables para reducir la confusión. n -> nf o nmain . x -> xf o xmain :
Cuando llama a la función f , el tiempo de ejecución de Python hace una copia de xmain y la asigna a xf , y de manera similar asigna una copia de nmain a nf .
En el caso de n , el valor que se copia es 1.
En el caso de x, el valor que se copia no es la lista literal [0, 1, 2, 3] . Es una referencia a esa lista. xf y xmain apuntan a la misma lista, por lo que cuando modifica xf también modifica xmain .
Sin embargo, si escribieras algo como:
encontrará que xmain no ha cambiado. Esto se debe a que, en la línea xf = ["foo", "bar"] , debe cambiar xf para que apunte a una nueva lista. Cualquier cambio que realice en esta nueva lista no tendrá efectos en la lista a la que xmain todavía apunta.
Espero que ayude. :-)
fuente
nf = 2
, dondenf
se cambia el nombre para señalar2
. Los números son inmutables, las listas son mutables.Es porque una lista es un objeto mutable. No está configurando x al valor de [0,1,2,3], está definiendo una etiqueta para el objeto [0,1,2,3].
Debería declarar su función f () así:
fuente
x = x + [4]
lugar dex.append(4)
, no vería ningún cambio en la persona que llama, aunque una lista es mutable. Tiene que ver con si realmente está mutado.x += [4]
entoncesx
está mutado, al igual que lo que sucedex.append(4)
, por lo que la persona que llama verá el cambio.n es un int (inmutable) y se pasa una copia a la función, por lo que en la función está cambiando la copia.
X es una lista (mutable), y se pasa una copia del puntero a la función, por lo que x.append (4) cambia el contenido de la lista. Sin embargo, usted dijo que x = [0,1,2,3,4] en su función, no cambiaría el contenido de x en main ().
fuente
Si las funciones se reescriben con variables completamente diferentes y llamamos a id en ellas, entonces ilustra bien el punto. Al principio no entendí esto y leí la publicación de jfs con la gran explicación , así que traté de entenderme / convencerme a mí mismo:
z y x tienen la misma identificación. Solo etiquetas diferentes para la misma estructura subyacente que dice el artículo.
fuente
Python es un lenguaje puro de paso por valor si lo piensa de la manera correcta. Una variable python almacena la ubicación de un objeto en la memoria. La variable Python no almacena el objeto en sí. Cuando pasa una variable a una función, está pasando una copia de la dirección del objeto al que apunta la variable.
Contrasta estas dos funciones
Ahora, cuando escribes en el shell
Compara esto con goo.
En el primer caso, pasamos una copia de la dirección de vaca a foo y foo modificó el estado del objeto que reside allí. El objeto se modifica.
En el segundo caso, pasa una copia de la dirección de vaca a goo. Entonces goo procede a cambiar esa copia. Efecto: ninguno.
Yo llamo a esto el principio de la casa rosa . Si haces una copia de tu dirección y le dices a un pintor que pinte la casa en esa dirección de rosa, terminarás con una casa rosa. Si le da al pintor una copia de su dirección y le dice que la cambie a una nueva dirección, la dirección de su casa no cambia.
La explicación elimina mucha confusión. Python pasa las direcciones de las variables almacenadas por valor.
fuente
Python es copia por valor de referencia. Un objeto ocupa un campo en la memoria, y una referencia está asociada con ese objeto, pero en sí mismo ocupa un campo en la memoria. Y el nombre / valor está asociado con una referencia. En la función python, siempre copia el valor de la referencia, por lo que en su código, n se copia para que sea un nombre nuevo, cuando lo asigna, tiene un nuevo espacio en la pila de llamadas. Pero para la lista, el nombre también se copió, pero se refiere a la misma memoria (ya que nunca asigna un nuevo valor a la lista). ¡Eso es una magia en python!
fuente
Mi comprensión general es que cualquier variable de objeto (como una lista o un dict, entre otros) se puede modificar a través de sus funciones. Lo que creo que no puede hacer es reasignar el parámetro, es decir, asignarlo por referencia dentro de una función invocable.
Eso es consistente con muchos otros idiomas.
Ejecute el siguiente script corto para ver cómo funciona:
fuente
Había modificado mi respuesta toneladas de veces y me di cuenta de que no tenía que decir nada, Python ya se había explicado.
Este demonio no es la referencia / valor / mutable o no / instancia, espacio de nombres o variable / lista o str, ES LA SINTAXIS, SIGNO IGUAL.
fuente