gpt4 book ai didi

python - 类属性评估和生成器

转载 作者:太空狗 更新时间:2023-10-29 17:06:43 25 4
gpt4 key购买 nike

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 的引用存储在该行的范围内。

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

最佳答案

是的,这有点狡猾。一个类并没有真正引入一个新的作用域,它只是看起来有点像它所做的;像这样的构造揭示了差异。

这个想法是,当您使用生成器表达式时,它等同于使用 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))

关于python - 类属性评估和生成器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1773636/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com