2009-06-10 7 views
11

Estoy leyendo How to think like a computer scientist que es un texto introductorio para "Programación de Python".Operador de multiplicación aplicado a la lista (estructura de datos)

Quiero aclarar el comportamiento del operador de multiplicación (*) cuando se aplica a las listas.

Considérese 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 cual 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 

provoca ... 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.

Respuesta

18

TODO en python son objetos, y python nunca hace copias a menos que se solicite explícitamente.

Al hacer

innerList = [0] * 10 

se crea una lista con 10 elementos, todos ellos los pasajes concernientes a la misma int objeto 0.

Dado que los objetos enteros son inmutables , 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 inmutabilidad de objetos.

Por eso

outerList = innerList * 5 

creará un objeto list con 5 elementos, cada uno es una referencia a la misma innerList al igual que anteriormente. Pero desde list objetos son mutables :

outerList[2].append('something') 

es lo mismo que:

innerList.append('something') 

Debido a que son dos referencias al objeto misma list. Entonces el elemento termina en ese solo list. Parece estar duplicado, pero el hecho es que solo hay un objeto list y muchas referencias al mismo.

Por el contrario si lo hace

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

Aquí están creando otralist objeto (usando + con las listas es una copia explícita), y asignar una referencia a él en la segunda posición de outerList . Si "agrega" el elemento de esta manera (no realmente se agrega, sino que crea otra lista), innerList no se verá afectado.

+0

suena perfecto, muchas gracias !! – fizzbuzz

+1

+1 - muy buena explicación –

-3

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

[[0] * columns] * rows 

Oops, acabamos de hacer una gran lista de punteros a [0]. Cambia uno y cámbialos a todos.

Los enteros no se pasan por referencia, realmente se copian, por lo tanto, los contenidos [0] * realmente están generando muchos NUEVOS 0 y agregándolos a la lista.

+0

aha, ¿Esto no parece un tipo especial de comportamiento para una lista de tamaño 1. Escuché que a un "Pythonista" no le gustan los casos especiales (como se explica en el Zen de Python "... Casos especiales aren No es lo suficientemente especial como para romper las reglas ... "). – fizzbuzz

+4

Engañoso. No existe tal cosa como "primitivo" en python. TODO es un objeto y se pasa por referencia todo el tiempo, INCLUYENDO INTEGERS. De hecho, las variables son simplemente referencias nominales. El problema aquí es que la lista es mutable, mientras que las entradas no lo son. – nosklo

+0

ejemplo: a = 5; b = a; print a es b # Que devuelve True porque a y b son referencias al mismo objeto. – nosklo

Cuestiones relacionadas