【问题标题】: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 的引用存储在行的范围内。

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

【问题讨论】:

    标签: python class attributes class-attributes


    【解决方案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@上的补丁讨论和替代补丁

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

      【讨论】:

        猜你喜欢
        • 2017-10-20
        • 1970-01-01
        • 1970-01-01
        • 2014-02-08
        • 2012-02-25
        • 1970-01-01
        • 2022-01-20
        • 1970-01-01
        相关资源
        最近更新 更多