类属性评估和生成器

Posted

技术标签:

【中文标题】类属性评估和生成器【英文标题】:Class attribute evaluation and generators 【发布时间】:2010-12-18 22:05:51 【问题描述】:

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]

(我的实际情况更复杂,我正在创建一个字典,但这是我能找到的最小示例。)

我唯一的猜测是列表推导是在该行计算的,但是生成器表达式是在类结束之后计算的,此时范围已经改变。但我不确定为什么生成器表达式不充当闭包并将对 base 的引用存储在行的范围内。

这是否有原因,如果有,我应该如何考虑类属性的评估机制?

【问题讨论】:

【参考方案1】:

是的,这有点狡猾。一个类并没有真正引入一个新的作用域,它只是看起来有点像它。像这样的结构暴露了差异。

这个想法是,当您使用生成器表达式时,它等同于使用 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 中消失了,这两种情况同样会失败。 Some discussion here.

可以使用 lambda 来解决问题,该技术与我们在nested_scopes 之前糟糕的过去所依赖的技术相同:

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

【讨论】:

【参考方案2】:

来自PEP 289:

在探索了许多可能性之后,一个 一致认为具有约束力的问题 很难理解,用户 应强烈鼓励使用 函数内部的生成器表达式 消耗他们的论点 立即地。对于更复杂的 应用程序,完整的发电机 定义总是优于 范围明显的术语, 生命周期和绑定 [6]。

[6] (1, 2) Source Forge http://www.python.org/sf/872326987654322@上的补丁讨论和替代补丁

据我所知,这就是生成器表达式的范围。

【讨论】:

以上是关于类属性评估和生成器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用实体 bean 类名和属性名生成类和属性?

JPA SequenceGenerator 和 GeneratedValue:名称/生成器属性仅每个类唯一?

论文泛读138BARTScore:将生成的文本评估为文本生成

java 注解处理器(AbstractProcessor) 获取到 指定注解的属性值 javapoet 如何使用这个值生成类?

如何使用 XSLT 生成可选的类属性值

使用免费负载生成器swingbench对oracle数据库进行压力测试(测试Oracle的功能或评估性能)