2012-12-26 89 views
5

在appengine應用程序中,我想爲對象列表構建一組所有屬性名稱。 這應該是相當簡單:在Python 2.7中使用嵌套的生成器表達式

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

然而,上述導致錯誤代碼:

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 

在調試一些實驗,我發現,添加僞表達修復了代碼後:

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

這對我來說非常不直觀,爲什麼原始版本在Python 2.7中失敗?以及爲什麼在中間添加「無用」表達可以解決問題?

+0

根據這個答案http://stackoverflow.com/questions/8049798/understanding-nested-list-comprehension該協會應該左到右,因此重新排序循環語句應該糾正。 – Ifthikhan

回答

7

只是改變計算的順序

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

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

你也並不需要一個列表理解,但有一組發電機理解表達會在這裏工作

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

評價的順序是從右到左

在原來的表達

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

可以分解爲

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

使用虛擬式的有趣的現象是,名單理解泄漏的變量,這會導致u在被泄露的事實全球範圍

所以

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

泄漏u在全球範圍內,

這使得

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

合法

下面的例子顯示,列表理解如何泄漏變量

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

感謝您的解釋,真的很有幫助。這是泄漏變量的錯誤? – Paul

+0

這不是一個Bug,而是Python 2.3中的一個特性。該功能已從Python 3.0中刪除。請參閱http://docs.python.org/2/reference/expressions.html#id20 – Abhijit

1

你原來的例子失敗的原因是你在錯誤的orde中有for子句河列表/生成器綜合中的for子句與將代碼作爲嵌套for循環編寫出來的順序相同。也就是說,最左邊的是最外面的,最右邊的是最裏面的。切換for子句的順序使其工作。

虛擬表達式改變行爲的原因是虛擬表達式是一個列表解析,而在Python 2中,列表解析(不同於生成器解析)將其循環變量泄漏到封閉範圍。泄露的u允許您的第二個示例運行,但它沒有按照您的想法運行,因爲props = ...行中的for子句仍然是錯誤的順序。它只循環一個值u,即虛擬表達式的最後一個u值。