2014-10-22 6 views
8

di lista hanno il loro codice inserito direttamente nella funzione dove sono utilizzati, in questo modo:Perché le espressioni generatrici e le spiegazioni dict/set in Python 2 usano una funzione annidata a differenza delle list comprehensions?

>>> dis.dis((lambda: [a for b in c])) 
    1   0 BUILD_LIST    0 
       3 LOAD_GLOBAL    0 (c) 
       6 GET_ITER    
     >> 7 FOR_ITER    12 (to 22) 
      10 STORE_FAST    0 (b) 
      13 LOAD_GLOBAL    1 (a) 
      16 LIST_APPEND    2 
      19 JUMP_ABSOLUTE   7 
     >> 22 RETURN_VALUE   

considerando espressioni elettrogeni e dict/set comprehensions principalmente sono collocati in una funzione annidato separato, in questo modo:

>>> dis.dis((lambda: {a for b in c})) 
    1   0 LOAD_CONST    1 (<code object <setcomp> at 0x7ff41a3d59b0, file "<stdin>", line 1>) 
       3 MAKE_FUNCTION   0 
       6 LOAD_GLOBAL    0 (c) 
       9 GET_ITER    
      10 CALL_FUNCTION   1 
      13 RETURN_VALUE   

>>> dis.dis((lambda: {a for b in c}).func_code.co_consts[1]) 
    1   0 BUILD_SET    0 
       3 LOAD_FAST    0 (.0) 
     >> 6 FOR_ITER    12 (to 21) 
       9 STORE_FAST    1 (b) 
      12 LOAD_GLOBAL    0 (a) 
      15 SET_ADD     2 
      18 JUMP_ABSOLUTE   6 
     >> 21 RETURN_VALUE   

In Python 3, tutti questi sono posizionati in una funzione nidificata.

Perché il codice viene inserito in una funzione nidificata separata? Ricordo vagamente di aver letto qualcosa su persone che volevano risolvere la comprensione e/o le variabili genexpr che si diffondevano nell'ambiente circostante molto tempo fa, era questa la soluzione per questo o qualcosa del genere?

Perché le implementazioni delle liste sono implementate in modo diverso rispetto al resto in Python 2? A causa della retrocompatibilità? (I ho pensato che la diffusione di sversamenti si è risolta molto dopo che sono state introdotte le espressioni generatrici, ma forse stavo leggendo vecchie discussioni o qualcosa del genere)

risposta

10

Sì, hai ragione. In Python 3.x, questo è stato introdotto per correggere la perdita di variabili. Citando dalla carica di History of Python blog, presumibilmente scritto dal BDFL se stesso,

Abbiamo anche fatto un altro cambiamento in Python 3, per migliorare equivalenza tra list comprehension e le espressioni del generatore. In Python 2, tabulato comprensione "fughe" la variabile di controllo nel perimetro circostante:

x = 'before' 
a = [x for x in 1, 2, 3] 
print x # this prints '3', not 'before' 

Questo era un artefatto della implementazione originale comprehensions lista; era uno degli "sporchi piccoli segreti" di Python per anni. È iniziato come un compromesso intenzionale per rendere le descrizioni delle liste incredibilmente veloci, e anche se non era una trappola comune per i principianti, sicuramente faceva male alla gente ogni tanto. Per le espressioni generatrici non potremmo farlo. Le espressioni del generatore sono implementate usando generatori, la cui esecuzione richiede un frame di esecuzione separato. Pertanto, le espressioni del generatore (specialmente se iterano su una breve sequenza) erano meno efficienti delle comprensioni di lista.

Tuttavia, in Python 3, abbiamo deciso di risolvere lo "sporco piccolo segreto" delle list comprehensions utilizzando la stessa strategia di implementazione delle espressioni di generatore. Quindi, in Python 3, l'esempio sopra (dopo la modifica per usare print(x) :-) stamperà 'prima', dimostrando che la 'x' nella comprensione della lista temporaneamente ombreggia ma non sovrascrive la 'x' nell'ambiente circostante.

A tutte le vostre domande viene data risposta dal testo evidenziato.

+2

Ecco [un link] (https://mail.python.org/pipermail/python-3000/2007-March/006017.html) per il thread in cui Nick Coghlan e Georg Brandl discutere il razionale per l'attuazione di loro in questo modo – Felipe

Problemi correlati