Multiplicar el operador aplica a la lista (estructura de datos)
-
13-09-2019 - |
Pregunta
Cómo pensar como un científico de la computación que es un texto introductorio de "programación Python" .
quiero aclarar el comportamiento del operador de multiplicar (*
) cuando se aplica a las listas.
Considere la función make_matrix
def make_matrix(rows, columns):
"""
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
return [[0] * columns] * rows
La salida real es
[[0, 7], [0, 7], [0, 7], [0, 7]]
La versión correcta de make_matrix es:
def make_matrix(rows, columns):
"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
matrix = []
for row in range(rows):
matrix += [[0] * columns]
return matrix
La razón por la primera versión de make_matrix falla (como se explica en el libro en 9.8) es que
... cada fila es un alias de las otras filas ...
Me pregunto por qué
[[0] * columns] * rows
... cada fila es un alias de las otras filas ...
pero no
[[0] * columns]
es decir. por qué cada [0]
en una fila no es un alias de otro elemento fila.
Solución
todo en Python son objetos, y Python no hace copias menos que explícitamente pidió que lo hiciera.
Al hacer
innerList = [0] * 10
se crea una lista con 10 elementos, todos ellos refiriéndose al mismo objeto int
0
.
Dado que los objetos son enteros inmutable , cuando lo hace
innerList[1] = 15
Está cambiando el segundo elemento de la lista para que se refiera a otro número entero 15
. Eso siempre funciona debido a int
objetos inmutabilidad.
Es por eso
outerList = innerList * 5
creará un objeto list
con 5 elementos, cada uno es una referencia a el mismo innerList
al igual que anteriormente. Pero ya que los objetos son list
mutable
outerList[2].append('something')
Es lo mismo que:
innerList.append('something')
Debido a que son dos referencias a la mismo objeto list
. Por lo que el elemento termina en esa única list
. Parece ser duplicado, pero el hecho es que no es sólo un objeto list
, y muchas referencias a él.
Por el contrario si lo hace
outerList[1] = outerList[1] + ['something']
Aquí está Creación de otro objeto list
(usando +
con las listas es una copia explícita), y asignar una referencia a él en la segunda posición de outerList
. Si "añadir" el elemento de esta manera (no es realmente añadiendo, pero la creación de otra lista), innerList
no se verá afectada.
Otros consejos
listas no son primitivas, que se pasan por referencia. Una copia de una lista es un puntero a una lista (en C jerga). Cualquier cosa que haga a la lista pasa a todas las copias de la lista y las copias de su contenido a menos que haga una copia superficial.
[[0] * columns] * rows
Vaya, acabamos de hacer una gran lista de punteros a [0]. Cambiar una y cambia a todos.
Los enteros no se pasan por referencia, que realmente están copiados, por lo tanto, [0] * contenidos realmente está haciendo un montón de nuevas 0 y añadiéndolos a la lista.