Cuando escribes [x]*3obtienes, 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]*4cada 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] * 4cada vez por la misma razón [x**2 for x in range(3)]reevalúax**2 cada vez. Cada evaluación de [1] * 4genera una nueva lista, por lo que la comprensión de la lista hace lo que quería.
Por cierto, [1] * 4tampoco copia los elementos de [1], pero eso no importa, ya que los enteros son inmutables. No puedes hacer algo así 1.value = 2y convertir un 1 en un 2.
[x]*3almacenar 3 referencias como[x, x, x]solo es correcto cuandoxes mutable. Esto no funciona, por ejemploa=[4]*3, para despuésa[0]=5,a=[5,4,4].[4]*3es esencialmente equivalente ax = 4; [x, x, x]. Sin embargo, es cierto que esto nunca causará ningún problema, ya que4es inmutable. Además, su otro ejemplo no es realmente un caso diferente.a = [x]*3; a[0] = 5no causará problemas incluso sixes 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
xrefiere. 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] * 2que amatrix = [row] * 2donde ambas filas son exactamente el mismo objeto, y ahora cambia a una fila dematrix[0][0] = yrepente se refleja en la otra(matrix[0][0] is matrix[1][0]) == TrueEn realidad, esto es exactamente lo que esperarías. Descompongamos lo que está sucediendo aquí:
Usted escribe
Esto es equivalente a:
Esto significa que
lstes 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.onesynp.zerosy / o para otro uso númeronp.repeat():fuente
Los contenedores de Python contienen referencias a otros objetos. Ver este ejemplo:
En esta
bes una lista que contiene un elemento que es una referencia a la listaa. La listaaes 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
cahora contiene dos referencias a la listaaque 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] * 3crea 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 aobjse reflejará en tres lugares, dondeobjse 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 . Aunque1es inmutable,obj =[1]*4seguirá creando una lista de14 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, entoncesobjse convertirán en[1,42,1,1]nocomo algunos pueden suponer. Esto también se puede verificar:[42,42,42,42]fuente
obj[2] = 42reemplaza 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
ids 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
xcuál es1, y una sola lista de elementos queycontienex. Su primer paso esy * 4obtener una nueva listaz, que es básicamente[x, x, x, x], es decir, crea una nueva lista que tendrá 4 elementos, que son referencias alxobjeto inicial . El paso neto es bastante similar. Básicamente lo hacesz * 3, que es[[x, x, x, x]] * 3y 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 myListY 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] * 2realmente 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:
*3tambié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]ylies mutable, por lo que cuando llamama[0][0]=1, ma [0] [0] es igual a&li[0], por lo que todas las&liinstancias 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])