python

title in python plot



En Python, ¿por qué una función puede modificar algunos argumentos tal como los percibe la persona que llama, pero no otros? (9)

Soy nuevo en Python y estoy tratando de entender su enfoque de alcance variable. En este ejemplo, ¿por qué f() puede alterar el valor de x , como se percibe dentro de 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]


Algunas respuestas contienen la palabra "copiar" en el contexto de una llamada a función. Lo encuentro confuso

Python no copia los objetos que pasa durante una llamada de función alguna vez .

Los parámetros de función son nombres . Cuando llamas a una función, Python vincula estos parámetros a cualquier objeto que pases (a través de nombres en el alcance de la persona que llama).

Los objetos pueden ser mutables (como listas) o inmutables (como enteros, cadenas en Python). Objeto mutable que puedes cambiar No puedes cambiar un nombre, solo puedes 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.

def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main() n = 2 # put `n` label on `2` balloon x.append(4) # call `append` method of whatever object `x` is referring to. print ''In f():'', n, x x = [] # put `x` label on `[]` ballon # x = [] has no effect on the original list that is passed into the function

Aquí hay buenas imágenes sobre la diferencia entre las variables en otros idiomas y los nombres en Python .


Cambiaré el nombre de las variables para reducir la confusión. n -> nf o nmain . x -> xf o xmain :

def f(nf, xf): nf = 2 xf.append(4) print ''In f():'', nf, xf def main(): nmain = 1 xmain = [0,1,2,3] print ''Before:'', nmain, xmain f(nmain, xmain) print ''After: '', nmain, xmain main()

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 está modificando xmain .

Sin embargo, si escribiera algo como:

xf = ["foo", "bar"] xf.append(4)

usted encontrará que xmain no ha cambiado. Esto se debe a que, en la línea xf = ["foo", "bar"] , tiene que cambiar xf para apuntar a una nueva lista. Cualquier cambio que realice en esta nueva lista no tendrá efectos en la lista a la que xmain aún apunta.

Espero que ayude. :-)


Es porque una lista es un objeto mutable. No está ajustando x al valor de [0,1,2,3], está definiendo una etiqueta para el objeto [0,1,2,3].

Deberías declarar tu función f () así:

def f(n, x=None): if x is None: x = [] ...


Python es copia por valor de referencia. Un objeto ocupa un campo en la memoria y una referencia está asociada con ese objeto, pero 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!


Python es un lenguaje puro paso por valor si lo piensas bien. Una variable python almacena la ubicación de un objeto en la memoria. La variable de 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.

Contraste estas dos funciones

def foo(x): x[0] = 5 def goo(x): x = []

Ahora, cuando escribes en el shell

>>> cow = [3,4,5] >>> foo(cow) >>> cow [5,4,5]

Compare esto con goo.

>>> cow = [3,4,5] >>> goo(cow) >>> goo [3,4,5]

En el primer caso, pasamos una copia de la dirección de cow to foo y foo modificamos el estado del objeto que reside allí. El objeto se modifica.

En el segundo caso, pasa una copia de la dirección de la vaca a goo. Luego, Goo procede a cambiar esa copia. Efecto: ninguno.

Yo llamo a esto el principio de la casa rosa . Si hace una copia de su dirección y le dice a un pintor que pinte la casa en esa dirección rosada, terminará con una casa rosada. 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 variables de direcciones almacenadas por valor.


Si las funciones se vuelven a escribir con variables completamente diferentes y llamamos id a ellas, entonces ilustra bien el punto. No entendí esto al principio y leí la publicación de jfs con la gran explicación , así que traté de comprenderme / convencerme a mí mismo:

def f(y, z): y = 2 z.append(4) print (''In f(): '', id(y), id(z)) def main(): n = 1 x = [0,1,2,3] print (''Before in main:'', n, x,id(n),id(x)) f(n, x) print (''After in main:'', n, x,id(n),id(x)) main() Before in main: 1 [0, 1, 2, 3] 94635800628352 139808499830024 In f(): 94635800628384 139808499830024 After in main: 1 [0, 1, 2, 3, 4] 94635800628352 139808499830024

z y x tienen la misma identificación. Solo etiquetas diferentes para la misma estructura subyacente como dice el artículo.


Ya tiene varias respuestas, y estoy de acuerdo con JF Sebastian, pero puede encontrar esto útil como un atajo:

Cada vez que ve varname = , está creando un nuevo enlace de nombre dentro del alcance de la función. Cualquier valor al que varname estuvo vinculado antes se pierde dentro de este alcance .

Cada vez que ve varname.foo() está llamando a un método en varname . El método puede alterar varname (ej. list.append ). varname (o, más bien, el objeto que varname names) 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 palabra clave global crea una excepción al primer caso]


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 para que x.append (4) cambie el contenido de la lista. Sin embargo, dijiste x = [0,1,2,3,4] en tu función, no cambiarías el contenido de x en main ().


f realidad no altera el valor de x (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 a la función. Pero como x es una referencia a una instancia de lista, solo se copia la referencia, no el contenido de la lista.

En caso de que esté familiarizado con C, lo siguiente se acerca a la semántica del código de Python:

void f(int n, int* x) { n = 42; x[0] = 2; }

Aquí, n es un int y x es un int* pero ambos se pasan como una copia a la función. No obstante, la memoria señalada por x es la misma a los lados de la persona que llama y del destinatario.