2

例:

class A:
    a = 1

class B(A):
    b = 2
    y = b # works fine
    x = a # NameError: name 'a' is not defined
    x = A.a # works fine

z = B()
z.a # works fine
B.a # works fine

なぜx = a許可されないのですか?他のすべてのコンテキスト (インスタンスを介したアクセス、サブクラス名を介したアクセス) では正常に動作します。しかし、どういうわけかクラス自体の中で、それは機能しません。

そして、この動作を考えると、各クラスがいくつかの追加属性を定義するクラス階層を実装できないようです-階層内のどこに定義されているかを正確に知らなければ、サブクラスでそれらにアクセスできないためです。

これが私が(失敗して)やろうとしていたことです:

class X:
  accelerate = compose(f1, f2, f3) # f1, f2, f3 are functions

class Y(X):
  move = compose(f4, f5)
  stop = f6

class Z(Y):
  action = compose(accelerate, stop)

class U(Y):
  action = compose(move, stop)

これらのクラスはまったく初期化されていません。それらを使用して関数の階層を作成したかっただけです。

4

3 に答える 3

7

これを書くとき:

class B(A):
    b = 2
    y = b # works fine
    x = a # NameError: name 'a' is not defined
    x = A.a # works fine

Python が行うことは、新しいスコープ (ディクショナリに格納されている) を作成し、すべての定義を実行してから、クラス ブロックの最後でtype(別のメタクラスを設定していない限り) クラスにディクショナリを渡して新しいクラスを作成することです。これは、これとほぼ同等です:

B = type('B', (A,), classdict_from_scope)

その段階に到達する前に、クラスBは存在せず、属性を継承する基本クラスはありません。複数の基本クラスを持つことができ、それらのクラスで検索される名前の解決順序は複雑であり、それらの完全なセットとそのすべての基本に依存することを考慮してください。この順序は、子クラスBが実際に作成されるまで決定されません。

これは、Python が executex = aになるとa、スコープ内にないことを検出し、クラスまたはインスタンスで属性ルックアップを行っていないため、名前解決プロトコルに従って別のバインディングを探すことができないことを意味します。したがって、できることはエラーをスローすることだけです。

そのため、 Python はそのように機能します。それについて何ができますか?

2 つの基本的なオプションがあります。

  1. 属性を検索するクラスを明示的に指定できます。
  2. サブクラスが作成された後、サブクラス内の属性を検索できます。

単一の継承のみを使用している場合、(1) はそれほど悪くないことに注意してください。のすべての属性を検索できますA。それらが thisの親クラスでのみ定義されている場合Aでもそれらを見つけることができるため、それらが定義されている階層内の場所を実際に知る必要はありません。

多重継承がある場合は、検索したい属性が含まれている階層を少なくとも知る必要があります。それが困難または不可能な場合は、次のようなオプション (2) を使用できます。

class B(A):
    b = 2
    y = b

B.x = B.a

これは少し見栄えが悪いですが、クラス ブロック内でB作成した場合と同じクラスが構築され、クラス ブロックの直後に代入を配置すると、クラス ブロックのない一時的なクラスは他のコードからは決して見えなくなります。(ただし、クラス デコレータを使用している場合は、同じ結果が得られない場合があります)xBxB.x

于 2012-01-25T06:48:35.987 に答える
2

クラス メンバーの定義classには、(ブロック内にあるため) クラス名を前に付ける必要はありませんが、付けますaccesses(何にアクセスしたいのかが明確でないため)。

b既にそのコード ブロックのローカル スコープ内にあるため (そこで定義されています)、アクセスできます。aではありません; クラスが完全に定義された後にのみ存在します。

なぜあなたはする必要がありますx = aか?あるメンバー変数の値を別のメンバー変数に基づいているのはなぜですか? 本当に必要な場合__init__は、クラスの関数を使用して値をコピーできます (実行時にサブクラスが完全に定義されているため__init__)。

于 2012-01-25T06:18:51.873 に答える
1

ベンの答えは、何が起こっているのかを説明するのに問題ありません。

ただし、Python 3 で作業しているため、実行したいことを実行できるようにする機能が Python のメタクラスに追加されています。クラスのローカル ディクショナリを更新できます ("locals ()") クラス本体が解析される前。

必要なのは__prepare__、メタクラスでメソッドを使用することだけです。クラス名とそのベースがタプルとして渡され、本体クラスの解析に使用される辞書オブジェクトを返すことが期待されます。

class MetaCompose(type):
    @staticmethod
    def __prepare__(name, bases):
        dct = {}
        for base in reversed(bases):
            dct.update(base.__dict__)
        return dct


class A:
    a = 1

class B(A, metaclass=MetaCompose):
    x = a


B.x

(参照http://docs.python.org/py3k/reference/datamodel.html#customizing-class-creation )

于 2012-01-25T11:12:05.030 に答える