Índices de listas anidadas [duplicado]
He experimentado algún problema al usar una lista anidada en Python en el código que se muestra a continuación.
Básicamente, tengo una lista 2D que contiene los 0 valores, quiero actualizar el valor de la lista en un bucle.
Sin embargo, Python no produce el resultado que quiero. ¿ Hay algo que no entiendo bien sobre range()
los índices de la lista de 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 son:
[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 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 dado que las listas pueden volverse bastante grandes, en lugar de desplazar toda la lista por la memoria, Python opta por utilizar simplemente una referencia ('puntero' en términos de C). Si asigna una a otra variable, le asigna solo la referencia. Esto significa que puedes tener dos variables apuntando 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]
hay 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 de tu lista son iguales, por lo que cambiar uno también cambia los demás:
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