在Python 2.7中使用嵌套生成器表达式

在Python 2.7中使用嵌套生成器表达式,python,google-app-engine,Python,Google App Engine,在appengine应用程序中,我想为对象列表构建一组所有属性名。 这应该相当简单: 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 allu

在appengine应用程序中,我想为对象列表构建一组所有属性名。 这应该相当简单:

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

这与我的直觉相反,为什么原始版本在Python2.7中失败了?为什么在中间添加一个“无用的”表达式来解决这个问题?

< P>只是改变评价顺序

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() )
评估的顺序是从右到左

用你原来的表情

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

这使得

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]
>>> 
>>deli
>>>foo=[范围(1,10)表示范围(10)]
>>>globals()['i']
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
globals()['i']
KeyError:“我很高兴。”
>>>[我代表我在福]
[[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]
>>> 

原始示例失败的原因是您的
for
子句顺序错误。列表/生成器理解中的
for
子句的顺序与将代码写成嵌套for循环时的顺序相同。也就是说,最左边的是最外面的,最右边的是最里面的。切换
for
子句的顺序以使其工作


伪表达式更改行为的原因是伪表达式是列表理解,在Python 2中,列表理解(与生成器理解不同)将其循环变量泄漏到封闭范围。泄漏的
u
允许第二个示例运行,但它没有按照您认为的那样运行,因为
props=…
行中的
for
子句的顺序仍然错误。它只在
u
的一个值上循环,即来自虚拟表达式的最后一个
u
值。

根据这个答案,关联应该是从左到右的,因此循环语句应该重新排序。感谢您的解释,真的很有帮助。变量泄漏是一个bug吗?这不是bug,而是Python 2.3之后的一个特性。此功能已从Python 3.0中删除。参考
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]
>>>