Índices de listas anidadas [duplicado]

Resuelto Ken Ma asked hace 12 años • 2 respuestas

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?

Ken Ma avatar Oct 25 '12 04:10 Ken Ma
Aceptado

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)]
D Read avatar Oct 24 '2012 22:10 D Read

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
Ashwini Chaudhary avatar Oct 24 '2012 21:10 Ashwini Chaudhary