2012-12-26 11 views
5

In un'applicazione appengine, voglio creare un insieme di tutti i nomi di proprietà per un elenco di oggetti. Questo dovrebbe essere abbastanza semplice:Utilizzo dell'espressione del generatore annidato in Python 2.7

users = security.User.all().fetch(1000) 
props = set([k for k in u.properties().keys() for u in users]) 

Tuttavia, il codice sopra i risultati in un errore:

File "/Users/paulkorzhyk/Projects/appengine-flask-template/app/app.py", line 70, in allusers 
props = set([k for k in u.properties().keys() for u in users]) 
UnboundLocalError: local variable 'u' referenced before assignment 

Dopo alcuni esperimenti in debugger ho notato che l'aggiunta di un'espressione fittizio corregge il codice:

users = security.User.all().fetch(1000) 
[u.properties().keys() for u in users] 
props = set([k for k in u.properties().keys() for u in users]) 

Questo è abbastanza controintuitivo per me, perché la versione originale non funziona in Python 2.7? e perché l'aggiunta di un'espressione "inutile" nel mezzo risolve il problema?

+0

Come da questa risposta http://stackoverflow.com/questions/8049798/understanding-nested-list-comprehension, l'associazione deve essere lasciata a destra e, quindi, riordinare le istruzioni del ciclo. – Ifthikhan

risposta

7

Basta cambiare l'ordine di valutazione

props = set([k for k in u.properties().keys() for u in users]) 

a

props = set([k for u in users for k in u.properties().keys() ]) 

anche che non è necessario un elenco di comprensione, ma l'espressione del generatore con una comprensione insieme avrebbe funzionato qui

props = set(k for u in users for k in u.properties().keys()) 

Ordine di valutazione da destra a sinistra

Nella tua espressione originale

set([k for k in u.properties().keys() for u in users]) 

può essere rotto come

for k in u.properties().keys(): # Here u is undefined 
    for u in users: 
     #what ever 

I fenomeni interessanti di usare un'espressione Dummy è il fatto che di lista Perdite variabili, che fa sì che u ad essere trapelato nella portata globale

Così

[u.properties().keys() for u in users] 

perdite u di portata globale,

che rende

set([k for k in u.properties().keys() for u in users]) 

legittimo

L'esempio seguente mostra, come di lista perdite variabili

>>> del i 
>>> foo = [range(1,10) for _ in range(10)] 
>>> globals()['i'] 

Traceback (most recent call last): 
    File "<pyshell#84>", line 1, in <module> 
    globals()['i'] 
KeyError: 'i' 
>>> [i for i in foo] 
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9]] 
>>> globals()['i'] 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> 
+0

Grazie per la spiegazione, davvero utile. Questa perdita di variabili è un bug? – Paul

+0

Questo non è un bug ma un post di funzionalità Python 2.3. Questa funzione è stata rimossa da Python 3.0. Consulta http://docs.python.org/2/reference/expressions.html#id20 – Abhijit

1

Il motivo per il tuo esempio originale non riesce è che hai le clausole for nell'oracolo sbagliato r. Le clausole for nelle comprensioni elenco/generatore sono nello stesso ordine in cui sarebbero se si scrivesse il codice come annidato per cicli. Cioè, quello più a sinistra è il più esterno, il più a destra è il più interno. Cambia l'ordine delle clausole for per farlo funzionare.

Il motivo per cui l'espressione fittizia cambia il comportamento è che l'espressione fittizia è una comprensione di lista, e in Python 2, le comprensibilità di lista (diversamente dalle generazioni del generatore) perdono la loro variabile di ciclo nell'ambito di chiusura.Lo u trapelato consente di eseguire il secondo esempio, ma non sta facendo ciò che si pensa, perché le clausole for nella riga props = ... sono ancora nell'ordine sbagliato. È solo il ciclo su un valore di u, vale a dire l'ultimo valore u dall'espressione dummy.