¿Por qué el operador + no cambia una lista mientras que .append () lo hace?

Estoy trabajando a través de Udacity y Dave Evans presentó un ejercicio sobre las propiedades de la lista

list1 = [1,2,3,4] list2 = [1,2,3,4] list1=list1+[6] print(list1) list2.append(6) print(list2) list1 = [1,2,3,4] list2 = [1,2,3,4] def proc(mylist): mylist = mylist + [6] def proc2(mylist): mylist.append(6) # Can you explain the results given by the four print statements below? Remove # the hashes # and run the code to check. print (list1) proc(list1) print (list1) print (list2) proc2(list2) print (list2) 

La salida es

 [1, 2, 3, 4, 6] [1, 2, 3, 4, 6] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4, 6] 

Entonces, en una función, la adición de un 6 al conjunto no se muestra, pero sí cuando no está en una función.

Entonces, en una función, la adición de un 6 al conjunto no se muestra, pero sí cuando no está en una función.

No, eso no es lo que pasa.

Lo que sucede es que cuando ejecutas mylist = mylist + [6] , estás creando efectivamente una lista completamente nueva y colocándola en la variable mylist local. Esta variable mylist desaparecerá después de la ejecución de la función y la lista recién creada también desaparecerá.

OTOH cuando ejecuta mylist.append(6) no crea una nueva lista. Ya obtienes la lista en la variable mylist y agregas un nuevo elemento a esta misma lista. El resultado es que la lista (que también es señalada por list2 ) se modificará. La variable mylist desaparecerá de nuevo, pero en este caso modificó la lista original.

Veamos si una explicación más visual te puede ayudar 🙂

Que pasa cuando llamas proc()

Cuando escribe list1 = [1, 2, 3, 4, 5] está creando un nuevo objeto de lista (en el lado derecho del signo igual) y creando una nueva variable, list1 , que apuntará a este objeto.

Creando nueva instancia de lista y variable global

Luego, cuando llama a proc() , crea otra nueva variable, mylist , y dado que pasa list1 como parámetro, mylist apuntará al mismo objeto:

Método de llamada crea variable local

Sin embargo, la operación mylist + [6] crea un objeto de lista completamente nuevo cuyo contenido es el contenido del objeto señalado por mylist más el contenido del siguiente objeto de lista, es decir, [6] . Dado que atribuye este nuevo objeto a mylist , nuestro escenario cambia un poco y mylist ya no apunta al mismo objeto señalado por list1 :

mylist apunta a un nuevo objeto de lista

Lo que no he dicho es que mylist es una variable local : desaparecerá después del final de la función proc() . Entonces, cuando la ejecución de proc() terminó, el mylist se fue:

mylist se ha ido

Como ninguna otra variable apunta al objeto generado por mylist + [6] , también desaparecerá (ya que el recolector de basura * lo recogerá):

GC recoge la lista

Tenga en cuenta que, al final, el objeto apuntado por list1 no se cambia.

Que pasa cuando llamas proc2()

Todo cambia cuando llamas proc2() . Al principio, es lo mismo: creas una lista …

Creando nueva instancia de lista y variable global

… y páselo como parámetro a una función, que generará una variable local:

Método de llamada crea variable local

Sin embargo, en lugar de utilizar el operador de concatenación + , que genera una nueva lista, aplica el método append() a la lista existente. El método append() no crea un nuevo objeto ; en cambio, cambia la existente:

Anexar un valor a una lista

Una vez finalizada la función, la variable local desaparecerá, pero el objeto original señalado por ella y por list1 ya se modificará:

Todavia esta alterado

Como todavía está señalado por list1 , la lista original no se destruye.

EDITAR : si quieres echar un vistazo a todo esto que sucede antes de que tus ojos solo vayan a este simulador radicalmente asombroso :

introduzca la descripción de la imagen aquí

* Si no sabes qué es el recolector de basura … bueno, lo descubrirás poco después de comprender tu propia pregunta.

Las variables en python siempre se pueden considerar como referencias. Cuando llama a una función con un argumento, está pasando una referencia a los datos reales.

Cuando utiliza el operador de asignación ( = ), está asignando ese nombre para referirse a un objeto completamente nuevo. Entonces, mylist = mylist + [6] crea una nueva lista que contiene los contenidos antiguos de mylist, así como 6, y asigna la variable mylist para referirse a la nueva lista. list1 sigue apuntando a la lista anterior, así que nada cambia.

Por otro lado, cuando usa .append, eso es en realidad agregar un elemento a la lista a la que se refiere la variable, no está asignando nada nuevo a la variable. Así que tu segunda función modifica la lista a la que se refiere list2.

Normalmente, en el primer caso en la función proc , solo podría cambiar la lista global por asignación si declara

 global mylist 

Primero y no pasó mylist como parámetro. Sin embargo, en este caso obtendría un mensaje de error que mylist que mylist es global y local: el name 'mylist' is local and global . Lo que sucede en el proc es que se crea una lista local cuando tiene lugar la asignación. Como las variables locales desaparecen cuando finaliza la función, el efecto de cualquier cambio en la lista local no se propaga al rest del progtwig cuando se imprime posteriormente.

Pero en la segunda función proc2 está modificando la lista agregándola en lugar de asignársela, por lo que la palabra clave global no es necesaria y los cambios en la lista se muestran en otra parte.

Esto, de una forma u otra, es una pregunta muy común. Me dio un golpe al explicar el parámetro de Python que pasé hace un par de días . Básicamente, uno de estos crea una nueva lista y el otro modifica el existente. En este último caso, todas las variables que hacen referencia a la lista “ven” el cambio porque sigue siendo el mismo objeto.

En la tercera línea, hiciste esto.

 list1=list1+[6] 

Entonces, cuando hiciste lo siguiente después de la línea anterior,

 print (list1) 

que imprimió la lista1 que creó al inicio y el procedimiento proc, que agrega list1 + [6] que crea una nueva lista dentro de la función proc. Donde, cuando está agregando [6], no está creando una lista nueva sino que está agregando un elemento de lista a una lista ya existente.

Pero, tenga en cuenta. En la línea 7, volviste a crear la lista.

 list1 = [1,2,3,4] 

Ahora quería imprimir la lista1 llamándola explícitamente, lo que imprimirá la lista1 que reinicializó de nuevo pero no la anterior.

Además de las respuestas completas que ya se dieron, también vale la pena tener en cuenta que, si desea la misma syntax de aspecto:

 mylist = mylist + [6] 

… pero aún desea que la lista se actualice “en el lugar”, puede hacer:

 mylist += [6] 

Lo que, aunque parece que haría lo mismo que la primera versión, es en realidad lo mismo que:

 mylist.extend([6]) 

(Tenga en cuenta que extend toma el contenido de un iterable y los agrega uno por uno, mientras que el append toma lo que se le da y lo agrega como un solo elemento. Vea el apéndice frente a la extensión para obtener una explicación completa).