Cuando escribes [x]*3
obtienes, esencialmente, la lista [x, x, x]
. Es decir, una lista con 3 referencias a la misma x
. Cuando modifica este single x
, es visible mediante las tres referencias:
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
Para solucionarlo, debe asegurarse de crear una nueva lista en cada posición. Una forma de hacerlo es
[[1]*4 for _ in range(3)]
que se reevaluará [1]*4
cada vez en lugar de evaluarlo una vez y hacer 3 referencias a 1 lista.
Quizás se pregunte por qué *
no puede hacer objetos independientes como lo hace la comprensión de la lista. Eso es porque el operador de multiplicación *
opera en objetos, sin ver expresiones. Cuando usa *
para multiplicar [[1] * 4]
por 3, *
solo ve que la lista de 1 elemento [[1] * 4]
evalúa, no[[1] * 4
texto de expresión. *
no tiene idea de cómo hacer copias de ese elemento, no tiene idea de cómo reevaluar [[1] * 4]
, y ni siquiera quiere copias, y en general, puede que ni siquiera haya una forma de copiar el elemento.
La unica opcion *
tiene es hacer nuevas referencias a la sublista existente en lugar de intentar hacer nuevas sublistas. Cualquier otra cosa sería inconsistente o requeriría un rediseño importante de las decisiones fundamentales de diseño del lenguaje.
Por el contrario, una comprensión de la lista reevalúa la expresión del elemento en cada iteración. [[1] * 4 for n in range(3)]
reevalúa [1] * 4
cada vez por la misma razón [x**2 for x in range(3)]
reevalúax**2
cada vez. Cada evaluación de [1] * 4
genera una nueva lista, por lo que la comprensión de la lista hace lo que quería.
Por cierto, [1] * 4
tampoco copia los elementos de [1]
, pero eso no importa, ya que los enteros son inmutables. No puedes hacer algo así 1.value = 2
y convertir un 1 en un 2.
[x]*3
almacenar 3 referencias como[x, x, x]
solo es correcto cuandox
es mutable. Esto no funciona, por ejemploa=[4]*3
, para despuésa[0]=5
,a=[5,4,4].
[4]*3
es esencialmente equivalente ax = 4; [x, x, x]
. Sin embargo, es cierto que esto nunca causará ningún problema, ya que4
es inmutable. Además, su otro ejemplo no es realmente un caso diferente.a = [x]*3; a[0] = 5
no causará problemas incluso six
es mutable, ya que no está modificandox
, solo modificaa
. No describiría mi respuesta como engañosa o incorrecta: simplemente no puede dispararse en el pie si se trata de objetos inmutables.x = 1000; lst = [x]*2; lst[0] is lst[1]
->True
. Python no distingue entre objetos mutables e inmutables aquí en absoluto.Tutor de Python en vivo Visualizar
fuente
x
refiere. Si comete un objeto único a nivel mundial conx = object()
y luego hacermatrix = [[x] * 2]
estos se vienen como verdadera:matrix[0][0] is matrix[0][1]
list
) así que si arow = [x] * 2
que amatrix = [row] * 2
donde ambas filas son exactamente el mismo objeto, y ahora cambia a una fila dematrix[0][0] = y
repente se refleja en la otra(matrix[0][0] is matrix[1][0]) == True
En realidad, esto es exactamente lo que esperarías. Descompongamos lo que está sucediendo aquí:
Usted escribe
Esto es equivalente a:
Esto significa que
lst
es una lista con 3 elementos que apuntan todoslst1
. Esto significa que las dos líneas siguientes son equivalentes:Como
lst[0]
es nada máslst1
.Para obtener el comportamiento deseado, puede usar la comprensión de la lista:
En este caso, la expresión se vuelve a evaluar para cada n, lo que lleva a una lista diferente.
fuente
id(lst[0][0])
yid(lst[1][0])
ni siquieraid(lst[0])
yid(lst[1])
o incluso:
Crea una lista que hace referencia a las
[1,1,1,1]
3 veces internas , no tres copias de la lista interna, por lo que cada vez que modifique la lista (en cualquier posición), verá el cambio tres veces.Es lo mismo que este ejemplo:
donde probablemente sea un poco menos sorprendente.
fuente
Junto con la respuesta aceptada que explicaba el problema correctamente, dentro de su comprensión de la lista, si está utilizando python-2.x, el uso
xrange()
que devuelve un generador que es más eficiente (range()
en python 3 hace el mismo trabajo) en_
lugar de la variable desechablen
:Además, como una forma mucho más pitónica que puede usar
itertools.repeat()
para crear un objeto iterador de elementos repetidos:EP Utilizar numpy, si sólo desea crear una serie de unos o ceros se pueden utilizar
np.ones
ynp.zeros
y / o para otro uso númeronp.repeat()
:fuente
Los contenedores de Python contienen referencias a otros objetos. Ver este ejemplo:
En esta
b
es una lista que contiene un elemento que es una referencia a la listaa
. La listaa
es mutable.La multiplicación de una lista por un número entero es equivalente a agregarse la lista varias veces (ver operaciones de secuencia comunes ). Continuando con el ejemplo:
Podemos ver que la lista
c
ahora contiene dos referencias a la listaa
que es equivalente ac = b * 2
.Las preguntas frecuentes de Python también contienen una explicación de este comportamiento: ¿Cómo creo una lista multidimensional?
fuente
myList = [[1]*4] * 3
crea un objeto de lista[1,1,1,1]
en la memoria y copia su referencia 3 veces. Esto es equivalente aobj = [1,1,1,1]; myList = [obj]*3
. Cualquier modificación aobj
se reflejará en tres lugares, dondeobj
se haga referencia en la lista. La declaración correcta sería:o
Lo importante a tener en cuenta aquí es que el
*
operador se utiliza principalmente para crear una lista de literales . Aunque1
es inmutable,obj =[1]*4
seguirá creando una lista de1
4 veces repetidas para formar[1,1,1,1]
. Pero si se hace alguna referencia a un objeto inmutable, el objeto se sobrescribe con uno nuevo.Esto significa que si lo hacemos
obj[1]=42
, entoncesobj
se convertirán en[1,42,1,1]
nocomo algunos pueden suponer. Esto también se puede verificar:[42,42,42,42]
fuente
obj[2] = 42
reemplaza la referencia en el índice2
, en lugar de mutar el objeto al que hace referencia ese índice, que es lo quemyList[2][0] = ...
hace (myList[2]
es una lista, y la asignación altera la referencia en el índice 0 en esa lista). Por supuesto, los números enteros no son mutables, pero un montón de tipos de objetos son . ¡Y tenga en cuenta que la[....]
notación de visualización de la lista también es una forma de sintaxis literal! No confunda objetos compuestos (como listas) y escalares (como enteros) con objetos mutables o inmutables.En palabras simples, esto está sucediendo porque en Python todo funciona por referencia , así que cuando creas una lista de esa manera, básicamente terminas con tales problemas.
Para resolver su problema, puede hacer cualquiera de ellos: 1. Use la documentación de la matriz numpy para numpy.empty 2. Agregue la lista a medida que llega a una lista. 3. También puedes usar el diccionario si quieres
fuente
Permítanos reescribir su código de la siguiente manera:
Luego de esto, ejecute el siguiente código para que todo quede más claro. Lo que hace el código es básicamente imprimir la
id
s de los objetos obtenidos, quey nos ayudará a identificarlos y analizar lo que sucede:
Y obtendrá el siguiente resultado:
Así que ahora vamos paso a paso. Tienes
x
cuál es1
, y una sola lista de elementos quey
contienex
. Su primer paso esy * 4
obtener una nueva listaz
, que es básicamente[x, x, x, x]
, es decir, crea una nueva lista que tendrá 4 elementos, que son referencias alx
objeto inicial . El paso neto es bastante similar. Básicamente lo hacesz * 3
, que es[[x, x, x, x]] * 3
y regresa[[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, por la misma razón que para el primer paso.fuente
Supongo que todos explican lo que está sucediendo. Sugiero una forma de resolverlo:
myList = [[1 for i in range(4)] for j in range(3)]
print myList
Y luego tienes:
fuente
Tratando de explicarlo más descriptivamente,
Operación 1:
Operación 2:
¿Notó por qué la modificación del primer elemento de la primera lista no modificó el segundo elemento de cada lista? Eso es porque
[0] * 2
realmente es una lista de dos números, y una referencia a 0 no puede modificarse.Si desea crear copias clonadas, intente la Operación 3:
Otra forma interesante de crear copias clonadas, Operación 4:
fuente
@spelchekr de la multiplicación de la lista de Python: [[...]] * 3 hace 3 listas que se reflejan entre sí cuando se modifican y tuve la misma pregunta sobre "¿Por qué solo el * 3 externo crea más referencias mientras que el interno no? ¿Por qué no es todo 1s?
Aquí está mi explicación después de probar el código anterior:
*3
también crea referencias, pero sus referencias son inmutables, algo así como[&0, &0, &0]
, cuando cambiarli[0]
, no puede cambiar ninguna referencia subyacente de const int0
, por lo que puede cambiar la dirección de referencia a la nueva&1
;ma=[&li, &li, &li]
yli
es mutable, por lo que cuando llamama[0][0]=1
, ma [0] [0] es igual a&li[0]
, por lo que todas las&li
instancias cambiarán su primera dirección a&1
.fuente
Al usar la función de lista incorporada, puede hacer esto
fuente
a.insert(0,[5,1,1,1])