9

最近のプロジェクトでは、次のようなことを試みました (より複雑ですが、結果は同じです)。

class MetaA(type):
    def __new__(cls, name, args, kwargs):
        print kwargs["foo"]
        return type.__new__(cls, name, args, kwargs)

class A(object):
    __metaclass__ = MetaA
    foo = "bar"

class B(A):
    pass

私はこれを得る:

bar
Traceback (most recent call last):
  File "C:\Users\Thorstein\Desktop\test.py", line 10, in <module>
    class B(A):
  File "C:\Users\Thorstein\Desktop\test.py", line 3, in __new__
    print kwargs["foo"]
KeyError: 'foo'

クラス属性は継承されませんか? もしそうなら、上記と同様のフレームワークで可能な回避策はありますか?

編集:プログラムの実際の(単純化された)例を使用すると、私が何を意味するのかを理解しやすくなるかもしれません..

class GameObjectMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs["dark_color"] = darken(*attrs["color"])
        return type.__new__(cls, name, bases, attrs)


class GameObject(object):
    __metaclass__ = GameObjectMeta
    color = (255,255,255)   # RGB tuple

class Monster(GameObject):
    pass

基本的に、基本色で関数を実行して、クラスに保存されている暗い色を作成します (クラスの複数のインスタンスは同じ色を必要としますが、クラス階層が長くなります)。これがより意味をなすことを願っています..

4

2 に答える 2

4

それらを継承することは想定されていません。メタクラスは、基本クラスの属性ではなく、インスタンス化するクラスで定義された属性を受け取ります。メタクラス コンストラクターの要点は、クラス本体で実際に指定されているものにアクセスすることです。

実際、基本クラスの属性は実際にはサブクラスの「中に」はまったくありません。サブクラスを定義するときにそれらがサブクラスに「コピー」されるわけではなく、実際、作成時にサブクラスはそのスーパークラスが持つ可能性のある属性を「認識」しません。むしろ、 にアクセスする B.fooと、Python はまず B の属性を見つけようとし、見つからない場合はそのスーパークラスを調べます。これは、そのような属性を読み取ろうとするたびに動的に発生します。メタクラス メカニズムは、スーパークラスの属性にアクセスすることは想定されていません。これらは実際にはサブクラスには存在しないためです。

おそらく関連する問題は、 の定義__new__が正しくないことです。メタクラスへの引数は、__new__cls、name、base、および attrs です。3 番目の引数は、"args" ではなく、基本クラスのリストです (意図したものは何でも)。4 番目の引数は、クラス本体で定義された属性のリストです。継承された属性にアクセスしたい場合は、ベースを介して取得できます。

とにかく、あなたはこのスキームで何を達成しようとしていますか? おそらくもっと良い方法があります。

于 2012-09-08T08:14:52.140 に答える
0

__init__then の代わりにを使用__new__すると、基本クラスの属性を継承できます。

class MetaA(type):
    def __init__(self, name, bases, attr):
        print self.foo  # don't use attr because foo only exists for A not B
        print attr  # here is the proof that class creation does not follow base class attributes
        super(MetaA, self).__init__(name, base, attr)  # this line calls type(), but has no effect

class A(object):
    __metaclass__ = MetaA
    foo = 'bar'

class B(A):
    pass

A が作成されると、以下が返されます。

bar
{'__module__': '__main__', 'foo': 'bar', '__metaclass__': <class '__main__.MetaA'>}

B が作成されると、以下が返されます。

bar
{'__module__': '__main__'}

注意してください、これは @BrenBarn が彼の答えで言ったことです 、それは作成されたときでfooはありません。そのため、OP の質問のコードでKeyError が発生します。ただし、呼び出すと、検索が見つからない場合は、そのベースである IE が検索されます。attrBself.foofooBA

両方のクラスのメタクラスは、そのクラスを調べることで決定できます。

>>> print A.__class__
<class '__main__.MetaA'>
>>> print A.__class__
<class '__main__.MetaA'>

__init__割り当てと初期化B、印刷self.foo、およびattrインの際にメタクラス メソッドが呼び出されるという事実も、そのstdoutメタクラスBMetaA.

于 2013-11-20T19:09:04.587 に答える