2012-12-26 44 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值。