6

この回答に続いて、クラスが以下を使用して定義された後にクラスのメタクラスが変更される可能性があるようです*:

class MyMetaClass(type):
    # Metaclass magic...

class A(object):
    pass

A = MyMetaClass(A.__name__, A.__bases__, dict(A.__dict__))

関数の定義

def metaclass_wrapper(cls):
    return MyMetaClass(cls.__name__, cls.__bases__, dict(cls.__dict__))

このようにクラス定義にデコレータを適用できます。

@metaclass_wrapper
class B(object):
    pass

にメタクラスの魔法がかかっているようですが、属性BBありません。__metaclass__上記の方法は、クラスを定義して再定義しているにもかかわらず、メタクラスをクラス定義に適用する賢明な方法ですか、それとも単純に書く方がよいでしょうか

class B(object):
    __metaclass__ = MyMetaClass
    pass

2つの方法にはいくつかの違いがあると思います。


*注、リンクされた質問の元の回答は、次MyMetaClass(A.__name__, A.__bases__, A.__dict__)を返しますTypeError:

TypeError: type() 引数 3 は dict_proxy ではなく dict でなければなりません

(クラス定義)の__dict__属性には type があるようですが、のインスタンスの属性の型には type があります。どうしてこれなの?これは Python 2.x と 3.x の違いですか?Adict_proxy__dict__Adict

4

3 に答える 3

1

クラスには__metaclass__属性が設定されていません...設定したことがないためです!

どのメタクラスを使用するかは、通常、クラス ブロックに設定さた名前によって決まります。__metaclass__属性は__metaclass__メタクラスによって設定されません。__metaclass__したがって、メタクラスを設定して Python に理解させるのではなく、メタクラスを直接呼び出すと、__metaclass__属性は設定されません。

実際、通常のクラスはすべてメタクラスのインスタンスであるtypeため、メタクラスが常に__metaclass__そのインスタンスに属性を設定すると、すべてのクラスに__metaclass__属性が設定されます (ほとんどのクラスは に設定されますtype)。


私はあなたのデコレータのアプローチを使用しません。メタクラスが関与しているという事実 (およびどのメタクラスか) が不明瞭であり、定型文の 1 行のままであり、3 つの定義機能からクラスを作成し(name, bases, attributes)て、結果のクラスからそれらの 3 ビットを引き戻すだけで、単に面倒です。クラスを離れて、同じ 3 ビットから新しいクラスを作成します。

Python 2.x でこれを行う場合:

class A(object):
    __metaclass__ = MyMeta
    def __init__(self):
        pass

次のように記述した場合、ほぼ同じ結果が得られます。

attrs = {}
attrs['__metaclass__'] = MyMeta
def __init__(self):
    pass
attrs['__init__'] = __init__
A = attrs.get('__metaclass__', type)('A', (object,), attrs)

type実際には、メタクラスの競合があるかどうかを判断するためにすべてのベースを検索する必要があるため、メタクラスの計算はより複雑ですattrs__metaclass__metaclass は、 ではなく祖先のメタクラスですtype__metaclass__これは、デコレータの「ソリューション」が直接使用する場合とは異なると予想される状況の 1 つです。デコレータを使用するとメタクラスの競合エラーが発生する状況でデコレータを使用するとどうなるか正確には__metaclass__わかりませんが、快適であるとは思いません。

また、関連する他のメタクラスがある場合、メソッドは最初にそれらを実行し (名前、ベース、および属性が何であるかを変更する可能性があります!)、次にそれらをクラスから引き出し、それを使用して新しいクラスを作成します。これは、 を使用して得られるものとはかなり異なる可能性があります__metaclass__

実際の辞書を提供し__dict__ないことに関しては、それは単なる実装の詳細です。パフォーマンス上の理由から推測します。__dict__(非クラス)インスタンスの がクラスの と同じタイプでなければならないという仕様はないと思います__dict__(これはインスタンスでもあります;メタクラスのインスタンスです)。クラスの__dict__属性は「dictproxy」です。これにより、属性キーを であるかのように検索できますがdict、それでも ではありませんdicttype3 番目の引数の型にうるさいです。「辞書のような」オブジェクトだけでなく、実際の辞書が必要です(ダックタイピングを台無しにするのは残念です)。これは 2.x 対 3.x の問題ではありません。Python 3 は同じように動作しますが、より適切な文字列表現を提供します。dictproxy. Python 2.4 (これは、私がすぐに入手できる最も古い 2.x です) にもdictproxy、クラス__dict__オブジェクトのオブジェクトがあります。

于 2012-06-19T02:53:08.640 に答える