16

これがstackoverflowのものかどうかは完全にはわかりません。そうでない場合は修正してください。

つまり、次の内容の t.py があるとします。

class A(object):
    pass
print("A:", A)

class B(object):
    print("From B: A:", A)


class OuterClass(object):
    class AA(object):
        pass
    print("AA:", AA)

    class BB(object):
        print("From BB: AA:", AA)

そして今、それを実行します:$ python3 t.py

A: <class '__main__.A'>
From B: A: <class '__main__.A'>
AA: <class '__main__.AA'>
From BB: AA:
Traceback (most recent call last):
  File "t.py", line 9, in <module>
    class OuterClass(object):
  File "t.py", line 14, in OuterClass
    class BB(object):
  File "t.py", line 15, in BB
    print "From BB: AA:", AA
NameError: name 'AA' is not defined

ドキュメントから:

クラス定義は実行可能なステートメントです。継承リストがある場合は、まずそれを評価します。継承リストの各項目は、サブクラス化が可能なクラス オブジェクトまたはクラス タイプに評価される必要があります。クラスのスイートは、新しく作成されたローカル名前空間と元のグローバル名前空間を使用して、新しい実行フレームで実行されます (名前付けとバインドのセクションを参照)。(通常、スイートには関数定義のみが含まれます。) クラスのスイートの実行が終了すると、その実行フレームは破棄されますが、ローカル名前空間は保存されます。[4] 次に、基本クラスの継承リストと属性ディクショナリの保存されたローカル名前空間を使用して、クラス オブジェクトが作成されます。クラス名は、元のローカル名前空間でこのクラス オブジェクトにバインドされます。

したがって、私は動作を理解していますが、他の場所のようにスコープをレキシカルにしないことの背後にある理論的根拠は理解していません。それは「特別なケースはルールを破るほど特別ではない」に反します。classがたとえば と異なる動作をするのはなぜdefですか?

これは「実用性が純粋性に勝る」ケースですか?もしそうなら、その根拠は何ですか?最初は python 2.x のアーティファクトかもしれないと思っていましたが、上記のように、動作は python 3.3 にも存在します。

4

1 に答える 1

11

Wooble がコメントで指摘したように、クラス ブロック新しいスコープを作成します。問題は、クラス ブロック スコープ内の名前が、そのスコープ内にネストされたスコープにアクセスできないことです。これはドキュメントに記載されています:

クラス ブロックで定義される名前のスコープは、クラス ブロックに限定されます。メソッドのコード ブロックには拡張されません。関数スコープを使用して実装されるため、これには内包表記とジェネレーター式が含まれます。

私は今これのソースを見つけることができませんが、どこか (私は StackOverflow の質問にあると思います) に 1 つのもっともらしい根拠を見つけました: ネストされたブロック内でクラス def にアクセスできる場合、メソッド名は組み込みを含むグローバル関数を隠します。これにより、短く単純な名前のメソッドを持つクラスを作成するのが難しくなりますが、同じ名前の組み込み関数も使用します。たとえば、これはできませんでした:

class SomeDataStructure(object):
    def sum(self):
        return sum(self._data)
    def otherCalc(self):
        return sum(item for item in somethingElse)

クラス ブロックがメソッド内のスコープ内にある場合、内部sum呼び出しがメソッド名にアクセスし、組み込みsum関数の代わりにそのメソッドを呼び出すため、無限再帰が発生します。同様に、the メソッドなどの他のメソッドはotherCalc、独自に使用するためにグローバル sum 関数にアクセスできなくなりますが、常にメソッドを取得します。 別の SO の質問への回答でselfは、「Python でメソッドにアクセスするために使用することになっているためです」と説明しています。

さて、その引数は実際にはクラス内の関数に対してのみ意味があります。関数本体は実行されるときではなく、ステートメントが実行されるときにdefクラス本体実行されるためです。ただし、上で述べたことをこのブログ投稿classの概念と組み合わせると、合理的な根拠が得られると思います。つまり、ネストされたクラスは、サポートする価値のあるスタイルと見なされていませんでした。ただし、クラス内の関数はもちろんありふれたものです。クラス内の関数の使用例を処理する最も簡単な方法は、ブロックがその名前を渡さないようにすることでした。classネストされたスコープ。その名前をネストされたクラスに渡し、ネストされた関数には渡さなかった方が間違いなく良いでしょう。

于 2012-12-18T19:21:22.053 に答える