AFo*_*lia 15 python attributes class class-attributes
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]
Run Code Online (Sandbox Code Playgroud)
(我的实际案例更复杂,我正在制作一个词典,但这是我能找到的最小例子.)
我唯一的猜测是列表推导是在该行计算的,但生成器表达式是在类结束后计算的,此时范围已经改变.但我不确定为什么生成器表达式不作为闭包并将引用存储在行的作用域中.
这是否有原因,如果是这样,我应该如何考虑类属性的评估机制?
bob*_*nce 14
是的,这有点狡猾.一个类并没有真正引入一个新的范围,它只是看起来有点像它; 像这样的结构揭示了差异.
这个想法是,当你使用生成器表达式时,它等同于使用lambda:
class Brie(object):
base= 2
powers= map(lambda i: base**i, xrange(5))
Run Code Online (Sandbox Code Playgroud)
或明确作为函数语句:
class Brie(object):
base= 2
def __generatePowers():
for i in xrange(5):
yield base**i
powers= list(__generatePowers())
Run Code Online (Sandbox Code Playgroud)
在这种情况下,显然base不在范围内__generatePowers; 两者都有例外(除非你不幸的是也有一个base全局,在这种情况下你会得到一个错误).
由于有关如何评估它们的一些内部细节,列表推导不会发生这种情况,但是这种行为在Python 3中消失了,这两种情况都会失败.这里有一些讨论.
使用lambda的解决方法可以使用我们在nested_scopes之前的旧时代所依赖的相同技术:
class Brie(object):
base= 2
powers= map(lambda i, base= base: base**i, xrange(5))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
935 次 |
| 最近记录: |