2012-12-26 7 views
5

W aplikacji appengine chcę zbudować zestaw wszystkich nazw właściwości dla listy obiektów. ta powinna być dość prosta:Korzystanie z zagnieżdżonego wyrażenia generatora w Pythonie 2.7

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

Jednakże powyższy kod powoduje błąd:

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 

Po kilku eksperymentach w debuggera Zauważyłem, że dodanie obojętne wyrażenie rozwiązuje kod:

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]) 

jest to dość intuicyjne mi, dlaczego jest oryginalna wersja braku w Pythonie 2.7? i dlaczego dodanie "bezużytecznego" wyrażenia w środku rozwiązuje problem?

+0

Zgodnie z tą odpowiedzią http://stackoverflow.com/questions/8049798/understanding-nested-list-comprehension powiązanie powinno być od lewej do prawej iw związku z tym zmiana kolejności instrukcji pętli powinna zostać poprawiona. – Ifthikhan

Odpowiedz

7

Wystarczy zmienić kolejność oceny

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

do

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

również nie trzeba się listowych ale wyrażenie generatora z zestawem pojmowania będzie działać tutaj

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

Kolejność oceny od prawej do lewej

W swojej oryginalnej ekspresji

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

można podzielić jako

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

Ciekawe zjawiska przy użyciu manekina wyrażenie jest fakt, że listowych Wycieki zmiennych, co powoduje u być wyciekły w zakres globalny

Więc

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

przecieki u w zakresie globalnym,

co sprawia

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

uzasadniony

Poniższy przykład pokazuje, w jaki sposób lista zrozumienie przecieki zmienne

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

Dzięki za wyjaśnienie, naprawdę pomocne. Czy ten wyciek zmiennych jest błędem? – Paul

+0

To nie jest błąd, ale funkcja po Pythonie 2.3. Ta funkcja została usunięta z Pythona 3.0. Zobacz http://docs.python.org/2/reference/expressions.html#id20 – Abhijit

1

Powodem oryginalny przykład nie jest to, że masz klauzule for w niewłaściwym orde r. Klauzule for w wyrażeniach listy/generatora są w tej samej kolejności, w jakiej zostałyby zapisane, jeśli napisałeś kod jako zagnieżdżony dla pętli. Oznacza to, że najbardziej wysunięty na lewo jest najbardziej zewnętrzny, a najbardziej prawy - najgłębszy. Zmień kolejność klauzul for, aby działało.

Powód, dla którego wyrażenie pozorne zmienia zachowanie, polega na tym, że wyrażenie pozorne jest rozumieniem listy, a w języku Python 2 (inaczej niż w przypadku wyrażeń generatora), ich zmienna pętli przechodzi do otaczającego zakresu.Wydzielony u pozwala na uruchomienie drugiego przykładu, ale nie robi tego, co myślisz, ponieważ twoje klauzule for w linii props = ... są nadal w niewłaściwej kolejności. Zapętla się tylko z jedną wartością o wartości u, a mianowicie ostatnią wartością u z fikcyjnego wyrażenia.

Powiązane problemy