[英]Class attribute evaluation and generators
Python如何评估类属性? 我偶然发现了一个有趣的怪癖(在Python 2.5.2中),我想解释一下。
我有一个类,其中一些属性是根据其他先前定义的属性定义的。 当我尝试使用生成器对象时,Python会抛出错误,但如果我使用普通的普通列表解析,那就没问题了。
这是精简的例子。 请注意,唯一的区别是Brie
使用生成器表达式,而Cheddar
使用列表推导。
# 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]
(我的实际案例更复杂,我正在制作一个词典,但这是我能找到的最小例子。)
我唯一的猜测是列表推导是在该行计算的,但生成器表达式是在类结束后计算的,此时范围已经改变。 但我不确定为什么生成器表达式不作为闭包并将引用存储在行的作用域中。
这是否有原因,如果是这样,我应该如何考虑类属性的评估机制?
是的,这有点狡猾。 一个类并没有真正引入一个新的范围,它只是看起来有点像它; 像这样的结构揭示了差异。
这个想法是,当你使用生成器表达式时,它等同于使用lambda:
class Brie(object):
base= 2
powers= map(lambda i: base**i, xrange(5))
或明确作为函数语句:
class Brie(object):
base= 2
def __generatePowers():
for i in xrange(5):
yield base**i
powers= list(__generatePowers())
在这种情况下,很明显, base
不在__generatePowers
范围内; 两者都有例外(除非你不幸的是还有一个base
全局,在这种情况下你会得到一个错误)。
由于有关如何评估它们的一些内部细节,列表推导不会发生这种情况,但是这种行为在Python 3中消失了,这两种情况都会失败。 这里有一些讨论。
使用lambda的解决方法可以使用我们在nested_scopes之前的旧时代所依赖的相同技术:
class Brie(object):
base= 2
powers= map(lambda i, base= base: base**i, xrange(5))
从PEP 289 :
在探索了许多可能性之后,出现了一种共识,即绑定问题很难理解,并且强烈建议用户在使用立即消耗其参数的函数内使用生成器表达式。 对于更复杂的应用程序,完整的生成器定义总是优于范围,生命周期和绑定[6]。
[6](1,2)关于Source Forge的补丁讨论和替代补丁http://www.python.org/sf/872326
就我所知,生成器表达式的作用方式就是这样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.