このよく知られている answer では、Pythonのメタクラスについて説明しています。 ___metaclass__
_属性は継承されないと記載されています。
しかし、実際のところ、私はPythonで試しました。
_class Meta1(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta1"
return type.__new__(cls, clsname, bases, dct)
# "Using Meta1" printed
class Foo1:
__metaclass__ = Meta1
# "Using Meta1" printed
class Bar1(Foo1):
pass
_
予想どおり、Foo
とBar
はどちらもメタクラスとして_Meta1
_を使用し、予想どおりに文字列を出力します。
ただし、次のサンプルでは、type(...)
の代わりにtype.__new__(...)
が返されると、メタクラスは継承されなくなります。
_class Meta2(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta2"
return type(clsname, bases, dct)
# "Using Meta2" printed
class Foo2:
__metaclass__ = Meta2
# Nothing printed
class Bar2(Foo2):
pass
_
___metaclass__
_属性と___class__
_属性を調べると、次のことがわかります。
_print Foo1.__metaclass__ # <class '__main__.Meta1'>
print Bar1.__metaclass__ # <class '__main__.Meta1'>
print Foo2.__metaclass__ # <class '__main__.Meta2'>
print Bar2.__metaclass__ # <class '__main__.Meta2'>
print Foo1.__class__ # <class '__main__.Meta1'>
print Bar1.__class__ # <class '__main__.Meta1'>
print Foo2.__class__ # <type 'type'>
print Bar2.__class__ # <type 'type'>
_
結論として:
___metaclass__
_と___class__
_の両方が基本クラスから継承されます。
_Meta2
_は実際にはtype
ですが、_Foo2
_で定義された作成動作は_Foo2.__class__
_に使用されます。
___metaclass__
_の_Bar2
_属性は_Meta2
_ですが、_Bar2
_の作成動作は影響を受けません。別の言葉で言えば、_Bar2
_は_Meta2
_の代わりにtype
を「実際の」メタクラスとして使用します。
これらの観察は、___metaclass__
_の継承メカニズムを私には曖昧にします。
私の推測はそれです:
クラス(例:_Meta1
_)を別のクラス 'Foo1'の___metaclass__
_属性に直接割り当てる場合、それは___metaclass__
_属性が有効になります。
サブクラスが定義時に___metaclass__
_属性を明示的に設定しない場合。基本クラスの___class__
_属性の代わりに___metaclass__
_属性が、サブクラスの「実際の」メタクラスを決定します。
私の推測は正しいですか? Pythonはメタクラスの継承をどのように処理しますか?
あなたは多くのことを推測していますが、Pythonのミニマリストと「特別な場合はルールを破るほど特別ではありません」。ディレクティブ、それよりも理解しやすくします。
Python2では、クラス本体の___metaclass__
_属性は、クラスの作成時に、そのクラスが存在する「クラス」を呼び出すために使用されます。通常、それはtype
という名前のクラスです。明確にするために、その瞬間は、パーサーがクラス本体を解析した後、コンパイラーがクラス本体をコンパイルした後、プログラムの実行時に実際に実行された後、___metaclass__
_が明示的に指定されている場合に限ります。クラス本体。
それでは、次のような場合に方法を確認しましょう。
_class A(object):
__metaclass__ = MetaA
class B(A):
pass
_
A
の本体には___metaclass__
_があります-MetaA
の代わりにtype
が呼び出され、「クラスオブジェクト」になります。 B
の本体には___metaclass__
_がありません。作成後、___metaclass__
_属性にアクセスしようとすると、他の属性と同じように表示されます。これは、PythonがスーパークラスA
から取得するためです。 _A.__dict__
_をチェックすると、___metaclass__
_が表示されますが、_B.__dict__
_をチェックしても表示されません。
この_A.__metaclass__
_属性は、Bが作成されたときに未使用です。 A
を宣言する前にB
で変更した場合でも、A
と同じメタクラスが使用されます-Pythonは、明示的な___metaclass__
_。
説明する:
_In [1]: class M(type): pass
In [2]: class A(object): __metaclass__ = M
In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
In [4]: class B(A): pass
In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
In [6]: A.__metaclass__ = type
In [8]: class C(A): pass
In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
_
さらに、type
ステートメントで本体を使用する代わりに、class
の呼び出しを介してクラスを作成しようとすると、___metaclass__
_も通常の属性になります。
_In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
_
これまでの要約:Python 2の___metaclass__
_属性は、実行の一部としてクラス本体宣言に明示的に配置されている場合にのみ特別ですclass
ブロックステートメントの。これは通常の属性であり、後で特別なプロパティはありません。
Python3は両方とも、この奇妙な「___metaclass__
_属性は今は良くない」を取り除き、メタクラスを指定するように構文を変更することでクラス本体をさらにカスタマイズできるようにしました。 (これは、metaclass
ステートメント自体で「class
namedparameter」であるかのように宣言されているようなものです)
さて、あなたの疑問を提起したものの2番目の部分に:メタクラスの___new__
_メソッドで_type.__new__
_の代わりにtype
を呼び出す場合、方法はありませんPython "know" type
は派生メタクラスから呼び出されています。_type.__new__
_を呼び出すときは、最初のパラメーターとしてcls
属性を渡します。メタクラスの___new__
_自体がランタイムによって渡されます。クラスはtype
のサブクラスのインスタンスです。これは、Pythonの他のクラスで継承が機能するのと同じです-したがって、ここでは「特別な動作はありません」:
だから、違いを見つける:
_class M1(type):
def __new__(metacls, name, bases, attrs):
cls = type.__new__(metacls, name, bases, attrs)
# cls now is an instance of "M1"
...
return cls
class M2(type):
def __new__(metacls, name, bases, attrs):
cls = type(cls, name, bases, attrs)
# Type does not "know" it was called from within "M2"
# cls is an ordinary instance of "type"
...
return cls
_
インタラクティブなプロンプトで確認できます。
_In [13]: class M2(type):
....: def __new__(metacls, name, bases, attrs):
....: return type(name, bases, attrs)
....:
In [14]: class A(M2): pass
In [15]: type(A)
Out[15]: type
In [16]: class A(M2): __metaclass__ = M2
In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
_
(メタクラス___new__
_メソッドの最初のパラメーターはメタクラス自体であるため、コードや「実際の」多くのコードでは、metacls
よりもcls
という名前が適切であることに注意してください)