Índices de listas anidadas

He experimentado algún problema al usar una lista anidada en Python en el código que se muestra.

Básicamente, tengo una lista 2D que contiene todos los valores 0, quiero actualizar el valor de la lista en un bucle.

Sin embargo, Python no produce el resultado que quiero. ¿Hay algo que mal entiendo sobre los índices de lista de range() y Python?

 some_list = 4 * [(4 * [0])] for i in range(3): for j in range(3): some_list[i+1][j+1] = 1 for i in range(4): print(some_list[i]) 

Los resultados que esperaba eran:

 [0, 0, 0, 0] [0, 1, 1, 1] [0, 1, 1, 1] [0, 1, 1, 1] 

Pero los resultados reales de Python son:

 [0, 1, 1, 1] [0, 1, 1, 1] [0, 1, 1, 1] [0, 1, 1, 1] 

¿Que está pasando aqui?

El problema se debe al hecho de que Python elige pasar las listas por referencia.

Normalmente las variables se pasan “por valor”, por lo que operan de forma independiente:

 >>> a = 1 >>> b = a >>> a = 2 >>> print b 1 

Pero como las listas pueden volverse bastante grandes, en lugar de cambiar la lista completa en la memoria, Python elige usar una referencia (“puntero” en C). Si asigna una a otra variable, solo le asigna la referencia. Esto significa que puede tener dos variables que apuntan a la misma lista en la memoria:

 >>> a = [1] >>> b = a >>> a[0] = 2 >>> print b [2] 

Entonces, en tu primera línea de código tienes 4 * [0] . Ahora [0] es un puntero al valor 0 en la memoria, y cuando lo multiplicas, obtienes cuatro punteros al mismo lugar en la memoria. PERO cuando cambias uno de los valores, Python sabe que el puntero debe cambiar para apuntar al nuevo valor:

 >>> a = 4 * [0] >>> a [0, 0, 0, 0] >>> [id(v) for v in a] [33302480, 33302480, 33302480, 33302480] >>> a[0] = 1 >>> a [1, 0, 0, 0] 

El problema surge cuando multiplicas esta lista: obtienes cuatro copias del puntero de la lista. Ahora, cuando cambia uno de los valores en una lista, los cuatro cambian juntos:

 >>> a[0][0] = 1 >>> a [[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]] 

La solución es evitar la segunda multiplicación. Un bucle hace el trabajo:

 >>> some_list = [(4 * [0]) for _ in range(4)] 

En realidad, todos los objetos en tu lista son iguales, así que cambiar uno cambia a otros también:

 In [151]: some_list = 4 * [(4 * [0])] In [152]: [id(x) for x in some_list] Out[152]: [148641452, 148641452, 148641452, 148641452] In [160]: some_list[0][1]=5 #you think you changed the list at index 0 here In [161]: some_list Out[161]: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]] #but all lists are changed 

Crea tu lista de esta manera:

 In [156]: some_list=[[0]*4 for _ in range(4)] In [157]: some_list Out[157]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] In [158]: [id(x) for x in some_list] Out[158]: [148255436, 148695180, 148258380, 148255852] In [163]: some_list[0][1]=5 In [164]: some_list Out[164]: [[0, 5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] #works fine in this case