类属性求值与生成器

2024-10-05 15:23:36 发布

您现在位置:Python中文网/ 问答频道 /正文

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的引用存储在行的作用域中。在

这样做有什么原因吗?如果有,我应该如何考虑类属性的评估机制?在


Tags: in列表forbase属性表达式stdinline
2条回答

来自PEP 289

After exploring many possibilities, a consensus emerged that binding issues were hard to understand and that users should be strongly encouraged to use generator expressions inside functions that consume their arguments immediately. For more complex applications, full generator definitions are always superior in terms of being obvious about scope, lifetime, and binding [6].

[6] (1, 2) Patch discussion and alternative patches on Source Forge http://www.python.org/sf/872326

这就是生成器表达式的作用域是如何确定的。在

是啊,这有点诡异。类并没有真正引入新的作用域,只是看起来有点像;像这样的构造暴露了差异。在

其思想是,当您使用生成器表达式时,它相当于使用lambda:

class Brie(object):
    base= 2
    powers= map(lambda i: base**i, xrange(5))

或显式作为函数语句:

^{pr2}$

在本例中,base显然不在__generatePowers的范围内;两者都会出现异常(除非您不幸地拥有一个base全局,在这种情况下,您得到了一个错误)。在

对于列表理解来说,这是不会发生的,因为它们的内部细节是如何计算的,但是这种行为在python3中消失了,这两种情况下都会同样失败。Some discussion here.

可以使用lambda来解决这个问题,该技术与我们以前在嵌套式_作用域之前所依赖的技术相同:

class Brie(object):
    base= 2
    powers= map(lambda i, base= base: base**i, xrange(5))

相关问题 更多 >