5

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

这对我来说非常违反直觉,为什么原始版本在 Python 2.7 中失败了?为什么在中间添加“无用”表达式可以解决问题?

4

2 回答 2

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

使用 Dummy 表达式的有趣现象是 List Comprehension Leaks Variables,这会导致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]
>>> 
于 2012-12-26T07:08:50.640 回答
1

您的原始示例失败的原因是您的for子句顺序错误。列表/生成器推导式中的for子句与将代码编写为嵌套 for 循环时的顺序相同。即最左为最外,最右为最内。切换for子句的顺序以使其工作。

虚拟表达式改变行为的原因是虚拟表达式是一个列表推导,而在 Python 2 中,列表推导(与生成器推导不同)将其循环变量泄漏到封闭范围。泄露的内容u允许您的第二个示例运行,但它并没有按照您的想法执行,因为您for在该行中的子句props = ...仍然是错误的顺序。它只循环一个 的值u,即u来自虚拟表达式的最后一个值。

于 2012-12-26T07:09:55.693 回答