2010-08-27 20 views
6

Quindi sto provando a generare un elenco annidato in Python basato su una larghezza e un'altezza. Questo è quello che ho finora:Python - Dynamic Nested List

width = 4 
    height = 5 
    row = [None]*width 
    map = [row]*height 

Ora, questo ovviamente non è giusto. Quando stampata si guarda bene:

[[None, None, None, None], 
[None, None, None, None], 
[None, None, None, None], 
[None, None, None, None], 
[None, None, None, None]] 

Ma il tentativo di assegnare un valore a una posizione in questo modo:

map[2][3] = 'foo' 

ottengo:

[[None, None, None, 'foo'], 
[None, None, None, 'foo'], 
[None, None, None, 'foo'], 
[None, None, None, 'foo'], 
[None, None, None, 'foo']] 

Chiaramente questo accade perché ogni sottoelenco è davvero basta fare riferimento allo stesso oggetto, riga, quindi cambiarne uno, cambiarli tutti. Quindi questo è il più vicino che ho!

Come posso generare dinamicamente un elenco annidato? Grazie!

+0

Anche se non è un duplicato esatto, vedi: http://stackoverflow.com/questions/1605024/python-using-the-multiply-operator-to -create-copie-di-oggetti-in-liste – carl

+2

'map()' è una funzione built-in, non è una buona idea sovrascriverla. Trova un altro nome, se necessario puoi semplicemente aggiungere '_'. –

risposta

11

Quando si esegue [row]*height si finisce con lo stesso oggetto elenco in ogni riga. Il riferimento dell'array row viene ripetuto in ogni riga, il che significa che ciascuna riga punta effettivamente allo stesso oggetto elenco. Quindi modificare una riga modifica effettivamente tutte le righe.

Dai un'occhiata a cosa succede quando si stampa il id() per ogni riga. Sono tutti uguali!

>>> grid = [[None] * width] * height 
>>> [id(row) for row in grid] 
[148014860, 148014860, 148014860, 148014860, 148014860] 

È possibile ottenere python per generare elenchi separati-ma-identico per ogni riga utilizzando un elenco di comprensione. Quando si utilizza [rowexpr for i in xrange(height)], il valore rowexpr verrà valutato una volta per riga. Il trucco è quindi usare un'espressione che si tradurrà in un elenco univoco ogni volta che viene valutata.

Questo farà più senso se lo vedi in azione:

>>> grid = [[None] * width for i in xrange(height)] 
>>> grid[2][3] = 'foo' 
>>> grid 
[[None, None, None, None], 
[None, None, None, None], 
[None, None, None, 'foo'], 
[None, None, None, None], 
[None, None, None, None]] 

Ogni volta che viene valutata [None] * width genera una nuova lista.

>>> [id(row) for row in grid] 
[148016172, 148015212, 148016236, 148016108, 148016332] 
0

io uso qualcosa di simile:

w = 5 
h = 5 

map = [] 

for i in range(h): 
row = [] 
for j in range(w): 
    row.append(None) 
map.append(row) 

print map 

map[2][3] = 'foo' 

print map 
+0

Una lista di comprensione è il __one__ giusto modo per farlo. – aaronasterling

+0

Sono d'accordo, la comprensione delle liste è più pulita, più leggibile, più "Pythonic". Il mio modo è solo un modo per risolvere il problema e anche leggibile per le persone (come me) che si stanno ancora evolvendo da C a Python. – lalli

+0

farlo con i cicli nidificati non aiuta te o nessun altro a fare la transizione da C a python: si mette di mezzo. – aaronasterling