2009-11-20 12 views
15

Pythonはどのようにクラス属性を正確に評価しますか?私は、私が説明したいと思う面白い変種(Python 2.5.2)を見つけました。クラス属性評価とジェネレータ

私は、他の定義済みの属性に関して定義されたいくつかの属性を持つクラスを持っています。ジェネレータオブジェクトを使用しようとすると、Pythonはエラーを投げますが、通常の普通のリスト理解を使用すると問題はありません。

ここでは例を示します。唯一の違いは、Cheddarはリストの理解度を使用しているのに対し、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] 

(私の実際のケースが複雑だった、と私は辞書を作成していたが、これは私が見つけることができる最小の一例です。)

私の唯一の推測では、リストの内包表記は、その行で計算されていることですただし、ジェネレータの式は、スコープが変更されたクラスの終了後に計算されます。しかし、なぜジェネレータの式がクロージャとして動作しないか分からず、ラインのスコープ内にベースへの参照を格納しています。

これには理由がありますか?その場合、クラス属性の評価の仕組みをどのように考える必要がありますか?

答えて

14

ええ、これはちょっと変わったものです。クラスは実際に新しいスコープを導入するわけではありません。このような構造は違いを露呈する。

がまたは明示的にfunctionステートメントとして
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ではなくなり、どちらの場合も同等に機能しなくなります。 PEP 289から

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

Some discussion here.

問題を回避するには、我々はnested_scopes前に悪い昔に戻っ依拠同じ手法でラムダを使用していたことができる多くの可能性を探索した後

、 コンセントが出現しました。バインディングの問題 は分かりにくく、ユーザ は、発電機の表現 を使用することを強く推奨します。内部の関数 は、その引数を消費します すぐに。より複雑な アプリケーションの場合、フルジェネレータ の定義は、有効範囲が であり、有効期間が であり、バインディング[6]が常に優れています。

[6](1、2)ソースフォージのパッチの議論と代替パッチhttp://www.python.org/sf/872326

それはジェネレータ式は限り私が作ることができるようにスコープされている方法です。

関連する問題