サブクラスのクラス変数は、親のクラス名を明示的に指定しないと親のクラス変数にアクセスできないことを知って驚いた。
>>> class A(object):
... x = 0
...
>>> class B(A):
... y = x+1
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in B
NameError: name 'x' is not defined
>>> class B(A):
... y = A.x + 1
...
>>> B.x
0
>>> B.y
1
なぜB.yを定義するときに、xではなくA.xを参照する必要があるのですか?これは、インスタンス変数からの直観に反します。Bが定義された後でB.xを参照できるからです。
Pythonでは、クラスが作成される前に、クラスの本体が独自の名前空間で実行されます(その後、その名前空間のメンバーがクラスのメンバーになります)。したがって、インタプリタがy = x + 1に到達したとき、クラスBはその時点ではまだ存在していないため、親はありません。
詳細については、 http://docs.python.org/reference/compound_stmts.html#class-definitions を参照してください
Pythonのベアネームのスコープルールは非常にシンプルで単純です。最初にローカルネームスペース、次に(存在する場合は)現在の関数がネストされている外部関数、次にグローバル、最後に組み込み関数です。これが、ベアネームが検索されたときに発生するすべてのことであり、複雑なルールを覚えたり適用したりする必要はありません(Pythonコンパイラがより複雑なルールを実施するために必要でもありません)。
別のルックアップが必要な場合は常に、ベアの名前ではなく修飾の名前を使用します。修飾名は、属性を要求できるオブジェクトに検索を常に委任でき、それらのオブジェクトが必要な検索ルールを実装できるため、はるかに強力です。特に、クラス内のインスタンスメソッドでは、self.x
はself
オブジェクトに検索を依頼する方法です属性名'x'
-そして、そのルックアップでは、継承の概念(および多重継承、メソッド解決順序など)の実装を含め、クラスに委任できます。
(クラスで定義されたメソッドの本体ではなく)クラスのbodyは、class
ステートメントの一部として実行されます。 beforeクラスオブジェクトが作成されるか、その名前がバインドされます(特に、いずれかのベースがベースとして定義される前に-とにかく、この最新の詳細は、ベアネームを参照するときには問題になりません!- )。
したがって、あなたの例では、クラスB
で、裸の名前x
がユニバーサルルールで検索されます-ローカルでバインドされた名前ですか?いいえの場合、このスコープがネストされている外部関数にバインドされていますか?いいえの場合、それはグローバルまたは組み込みとしてバインドされていますか?上記のいずれでもない場合は、問題のベアネームを使用すると、名前エラー例外が発生します。
ベアネームルックアップルールが普遍的に適用するものとは異なるルックアップシーケンスが必要なため、ベアネームではなく修飾名を使用する必要があることは明らかです。少し振り返ると、目的に使用する修飾名の「1つの明らかな選択肢」はA.x
でなければならないことが明確に示されます。これがwant検索する(ベースはどこにも記録されていませんまだその時点で、結局...それはメタクラス(通常はtype
)であり、クラス本体のafterが呼び出されたときに、ジョブの一部としてbases-bindingを実行します実行が完了しました!-)。
一部の人々は、ベアネームの検索に関する他の「魔法の」ルールに熱心に執着しているため、Python)のこの側面に我慢できません(もともとは、インスピレーションを受けて、Modula-3によって、理論家のサークルでよく検討されているほとんど知られていない言語;-)-x
を使用するのではなくself
で検索する必要があることを指定するメソッドにself.x
を記述する必要がありますたとえば、普遍的なベアネームルールは、そのような人々をバティにします。
私、ベアネームルックアップルールのシンプルさと普遍性が好きです。また、必要なときにいつでもベアネームの代わりに修飾名を使用するのが好きですany他のルックアップ形式...でも、私がPythonに夢中になっているのは秘密ではありません(たとえば、自分の不平を言う-global x
など)クロール、ここではむしろglobal.x
を記述します。つまり、global
を「現在実行中のモジュール」の組み込み名にします... I do love修飾名!-)、そうですか?-)