2015-07-15 4 views
5

Quando eseguoPerché questo codice Python (comporre un'estensione di lista con una mappa di se stesso) rende il mio sistema bloccato?

a = ['a'] 
a.extend(map(lambda x: 'b' + x, a)) 

si blocca il mio sistema fino a quando posso fare un Ctrl + C se corro come uno script Python dal guscio, e l'esecuzione dal interprete mi ha fatto avere per spegnere difficile la mia il computer portatile.

Tuttavia,

a = ['a'] 
a.extend(list(map(lambda x: 'b' + x, a))) 

funziona bene e dà il risultato atteso.

Perché succede?

In un primo momento, ho pensato che potrebbe essere perché stavo cercando di estendere a con una funzione di mappa che riceve un a stesso, così mi ha scritto:

a = ['a'] 
tmp = map(lambda x: 'b' + x, a) 
a.extend(tmp) 

tuttavia, che anche congelato.

Allo stesso modo, questo sembra funzionare bene:

a = ['a'] 
tmp = list(map(lambda x: 'b' + x, a)) 
a.extend(tmp) 

Perché succede questo?

Sto facendo questo su Python 3.4.3.

+0

Questo funziona bene in python 2.7. * Quindi suppongo che il problema sia legato al fatto che la funzione 'map' restituisce un' map object' in python 3 (diversamente da un elenco in python 2). – puzzlepalace

+0

Funziona bene per me sia su 3.4.3 che su 2.7.9 – alfasin

+0

Hum. La mia intuizione è la stessa di quella originale --- questo odora di un problema di mutevolezza di lista. E il fatto che entrambe le versioni funzionino quando sono racchiuse nella lista() (che crea una copia della lista) è un grande indizio per questo. La mia ipotesi è che entrambe le versioni senza il codice di lista stanno mutando la lista originale (con la seconda versione che lo fa tramite un puntatore) e la mappa continua ad estenderle, portando ad un loop infinito. Tuttavia, le conseguenze della mutabilità delle liste e le cose bizzarre che Python fa con i puntatori sono molto al di sopra del mio voto, quindi questo potrebbe essere sbagliato ... –

risposta

3

Questo perché in Python 3.La funzione x map() restituisce un iteratore, che utilizza il riferimento della lista passato ad esso come secondo parametro. Quindi, mentre stai iterando sopra l'iteratore della mappa, stai anche estendendo la lista, e questa continua all'infinito, quindi ottieni MemoryError o finisci con un ciclo infinito.

esempio per mostrare questo comportamento -

>>> m = map(lambda x: a.extend(x), a) 
>>> m 
<map object at 0x021E0E70> 
>>> for i,x in enumerate(m): 
...  print("Hello") 
... 
Hello 
Hello 
.... # lots of Hello 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 1, in <lambda> 
MemoryError 

Quindi, quando si fa - a.extend(map(lambda x: 'b' + x, a)). State facendo qualcosa di simile a -

a = ['a'] 
for x in a: 
    a.extend('b'+x) 

Se si tenta il codice di cui sopra, si continua a ottenere la o il ciclo infinito MemoryError.

Quando si esegue -

a.extend(list(map(lambda x: 'b' + x, a))) 

Si utilizza l'iteratore convertendolo in un elenco, prima di sta estendendo la lista a, quindi non finire in un ciclo infinito. In questo caso si sta facendo qualcosa di simile a-

a = ['a'] 
templist = [] 
for x in a: 
    templist.extend('b' + x) 
a.extend(templist) 

Ecco perché non si ottiene l'errore. Si prega di notare che il codice sopra potrebbe non essere il modo in cui python esegue internamente map, è solo qualcosa di simile.

2

In python 3, verrà generato un iteratore dalla funzione map().

Vedendo a.extend() funzione, pitone troverà che si desidera estendere la lista a con un iteratore relative al a e automaticamente aiutare a fare l'iterazione.

E qui inizia l'iterazione.

In primo luogo, è un 'a' in un. L'iteratore all'interno della funzione map()'a', un 'ba' viene generato dall'espressione lambda e viene aggiunto all'elenco a. a diventa ['a', 'ba'] ora.

Poi, l'iteratore interno map() funzione trova che l'iterazione di a non sta dando StopIteration causa a 's nuovo amico 'ba'' s ritornassi. Pertanto, l'iteratore all'interno della funzione map() fornisceper lambda da elaborare. A 'bba' viene generato qui e viene inserito in a.

Ecco come funziona la propagazione infinita di a.

Il seguente codice può aiutare:

a = ['a'] 
import time 
a.extend(map(lambda x: ('b' + x, print(x), time.sleep(1))[0], a)) 

E dovrebbe essere banale per capire perché il vostro usando di list() per trasformare l'iteratore in un elenco statico non innescare questo.

1

Penso meccanismo di gestione oggetto di Python è diversa da C/C++, vedere questo:

a = ['a'] 

for x in a: a.append('b')

se si digita nella riga di comando Python, si incontra un ciclo infinito, e dopo si immette CTRL + C, e

>>> a 

si otterrà un lungo elenco che contiene 'a' e 'b', e penso che nel ciclo for, l'una e l'una di a.append ('b') sono t lui stesso oggetto e nella stessa memoria. Questo è quello che penso.

+0

Quindi, quando una lista si aggiorna e 'per x in a' diventa più lunga e più lunga –

+0

'a.append ('b')' rende 'a' più grande e più grande, e quando arriva il ciclo successivo' a' in 'for x in a:' sono diversi dall'ultimo ciclo, perché è aggiunto a 'b '. Quindi questo è un ciclo infinito –