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]
(私の実際のケースはもっと複雑で、辞書を作成していましたが、これは私が見つけることができる最小限の例です。)
私の唯一の推測は、リスト内包はその行で計算されますが、ジェネレータ式はクラスの終了後に計算され、その時点でスコープが変更されています。しかし、ジェネレータ式がクロージャとして機能せず、ベースへの参照を行のスコープに格納しない理由はわかりません。
これには理由がありますか?もしそうなら、クラス属性の評価メカニズムをどのように考えるべきですか?