2011-05-10 9 views
17

Digamos que tengo el siguiente código:Python lista de confusión

a_list = [[0]*10]*10 

Esto genera la siguiente lista:

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

entonces quiero modificar el primer elemento de la primera lista:

a_list[0][0] = 23 

Esperaba que solo se modificara el primer elemento de la lista, pero en realidad se cambió el primer elemento de cada lista :

[[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0]] 

Me las arreglé para encontrar otra manera de representar mis datos para evitar esto, pero ¿por qué sucede esto? ¿Por qué no solo se cambia la primera lista? Cuando hago el segundo *10, ¿Python realmente copia la dirección de la primera lista en lugar de asignar un nuevo bloque de memoria?

+1

Sus sospechas son correctas. –

+3

¿La "confusión de la lista" es el gemelo menos conocido de la "lista de comprensión"? ;) –

Respuesta

13

Su corazonada sobre la copia de direcciones es correcta. Piénselo de esta manera:

sub_list = [0] * 10 
a_list = [sub_list] * 10 

Este código es en realidad equivalente al código que ha publicado anteriormente. Lo que esto significa es que en realidad está cambiando la misma lista sub_list siempre que cambie cualquier elemento de a_list. Incluso puede estar seguro de ello escribiendo:

a_list = [[0] * 10] * 10 
for n in a_list: 
    print id(n) 

Y se mostrará lo mismo para cada elemento. Para remediar esto, se debe utilizar:

a_list = [[0] * 10 for _ in range(10)] 

Con el fin de crear una nueva lista secundaria para cada elemento de a_list.

+0

Gracias. El primer ejemplo me ayudó a entender esto mejor. – yoshi

3

¿Por qué no se acaba de cambiar la primera lista?

La razón es simple, en realidad es sólo el 1 lista, no 10 - del mismo modo que ya sospechaba:

In [1]: [[0]*10]*10 
Out[1]: 
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] 

In [2]: map(id, _) 
Out[2]: 
[54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624] 

Si desea crear 10 listas, se puede lograr esto fácilmente a través de una expresión como

[[0]*10 for x in xrange(10)] 
4

Las listas contienen referencias a los objetos. La multiplicación en las listas simplemente repite las referencias (¡a los mismos objetos!). Si bien esto está bien para objetos inmutables (como números enteros), lo que está obteniendo es referencias múltiples a la misma lista .

Crear listas separadas con este patrón [[0]*10 for _ in xrange(10)].

+0

Pero entonces ¿por qué no sucede esto en esa primera lista, '[0] * 10'? Esta también es una lista de multiplicación, pero aparentemente aquí no es solo la referencia a ese primer '0' que se repite ...? –

+0

@RolfBartstra, it * es * la misma referencia a un objeto entero ** inmutable ** que se repite.No puede alterar el contenido de un objeto entero para que no pueda causar el mismo problema que con un objeto de lista mutable. –