Python如何精确地计算类属性?我偶然发现了一个有趣的怪癖(在python2.5.2中),我想解释一下。在
我有一个类,其中一些属性是根据其他先前定义的属性定义的。当我尝试使用生成器对象时,Python会抛出一个错误,但是如果我使用普通的列表理解,就没有问题了。在
这是一个精简的例子。注意,唯一的区别是Brie
使用生成器表达式,而{
# Using a generator expression as the argument to list() fails
>>> class Brie :
... base = 2
... powers = list(base**i for i in xrange(5))
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in Brie
File "<stdin>", line 3, in <genexpr>
NameError: global name 'base' is not defined
# Using a list comprehension works
>>> class Cheddar :
... base = 2
... powers = [base**i for i in xrange(5)]
...
>>> Cheddar.powers
[1, 2, 4, 8, 16]
# Using a list comprehension as the argument to list() works
>>> class Edam :
... base = 2
... powers = list([base**i for i in xrange(5)])
...
>>> Edam.powers
[1, 2, 4, 8, 16]
(我的实际情况更为复杂,我正在创建dict,但这是我能找到的最少的示例。)
我唯一的猜测是,列表理解是在那一行计算的,但是生成器表达式是在类结束后计算的,此时范围已更改。但我不确定为什么生成器表达式不作为闭包,并将对base的引用存储在行的作用域中。在
这样做有什么原因吗?如果有,我应该如何考虑类属性的评估机制?在
来自PEP 289:
这就是生成器表达式的作用域是如何确定的。在
是啊,这有点诡异。类并没有真正引入新的作用域,只是看起来有点像;像这样的构造暴露了差异。在
其思想是,当您使用生成器表达式时,它相当于使用lambda:
或显式作为函数语句:
^{pr2}$在本例中,
base
显然不在__generatePowers
的范围内;两者都会出现异常(除非您不幸地拥有一个base
全局,在这种情况下,您得到了一个错误)。在对于列表理解来说,这是不会发生的,因为它们的内部细节是如何计算的,但是这种行为在python3中消失了,这两种情况下都会同样失败。Some discussion here.
可以使用lambda来解决这个问题,该技术与我们以前在嵌套式_作用域之前所依赖的技术相同:
相关问题 更多 >
编程相关推荐