Operador multiplicado aplicado a la lista (estructura de datos)

Estoy leyendo Cómo pensar como un científico informático que es un texto introductorio para “Progtwigción Python“.

Quiero aclarar el comportamiento del operador 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 que 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 porque

 [[0] * columns] * rows 

causas … 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 de fila.

TODO en python son objetos, y python nunca hace copias a menos que se le pida explícitamente que lo haga.

Cuando tu lo hagas

 innerList = [0] * 10 

creas una lista con 10 elementos, todos ellos se refieren al mismo objeto int 0 .

Dado que los objetos enteros son inmutables , cuando lo haces

 innerList[1] = 15 

Está cambiando el segundo elemento de la lista para que se refiera a otro entero 15 . Eso siempre funciona debido a la inmutabilidad de los objetos int .

Es por eso

 outerList = innerList * 5 

Creará un objeto de list con 5 elementos, cada uno de ellos es una referencia a la misma lista innerList tal como se innerList anteriormente. Pero como los objetos de la list son mutables :

 outerList[2].append('something') 

Es lo mismo que:

 innerList.append('something') 

Porque son dos referencias al mismo objeto de list . Entonces el elemento termina en esa list única. Parece estar duplicado, pero el hecho es que solo hay un objeto de list y muchas referencias a él.

Por el contrario si lo haces

 outerList[1] = outerList[1] + ['something'] 

Aquí está creando otro objeto de list (el uso de + con listas es una copia explícita) y le asigna una referencia en la segunda posición de la lista outerList . Si “agrega” el elemento de esta manera (no se agrega realmente, sino que se crea otra lista), la lista innerList se verá afectada.

Las listas no son primitivas, se pasan por referencia. Una copia de una lista es un puntero a una lista (en la jerga C). Todo lo que hagas a la lista pasa con todas las copias de la lista y las copias de su contenido a menos que hagas una copia superficial.

 [[0] * columns] * rows 

Vaya, acabamos de hacer una gran lista de punteros a [0]. Cambia uno y tú los cambias a todos.

Los enteros no se pasan por referencia, realmente se copian, por lo tanto, el contenido de [0] * realmente está generando muchos NUEVOS 0 y los está agregando a la lista.