所以 --- 一个可以回答的有点令人困惑的问题,并且有些简化
只需在交互模式下运行一些示例即可。
但要开始,当您声明时:
type.__call__(...) in turn run two other methods (a __new__ and a __init__).
这是对发生的事情的简化。
当我们创建新类时,比如在解析类语句class A: 时,type.__call__ 会被调用。但是这个调用是在 Meta 本身的 class 中搜索的。即“Meta”的“元类”——默认为type。
请耐心等待:
当我们谈论一个没有自定义元类的普通类 E 时,您通过执行 E() 创建一个实例 - Python 在 E 是一个实例的类中搜索 __call__ 方法:即它的元类.因为它是类型,所以 type.__call__ 被调用。正如您所说,它是 type.__call__ 调用 __new__ 和 __init__ 方法,但不仅适用于元类:它在任何对象实例化中编排这些调用 - 在 Python 中的任何对象实例化中使用完全相同的机制:两者普通对象和类:
In [178]: class MetaMeta(type):
...: def __call__(metacls, *args, **kw):
...: print("Now at the meta-meta class")
...: return super().__call__(*args, **kw)
...:
In [179]: class EmptyMeta(type, metaclass=MetaMeta):
...: def __call__(cls, *args, **kw):
...: print("At the metaclass __call__")
...: return super().__call__(*args, **kw)
...:
...:
...:
In [180]: class A(metaclass=EmptyMeta):
...: pass
...:
Now at the meta-meta class
In [181]: a = A()
At the metaclass __call__
In [182]: class Direct(metaclass=MetaMeta): pass
In [183]: Direct()
Now at the meta-meta class
Out[183]: <__main__.Direct at 0x7fa66bc72c10>
所以,简而言之:在创建一个作为Meta实例的类A时,调用Meta类的__call__方法。这将在元类 Meta 中调用 __init__ 和 __new__。如果没有定义,普通的属性查找会在 Meta 的超类中调用这些方法,而 也 是“类型”。
现在,继续您的问题:当继承自具有自定义元类的类时,例如您的 B 类,Python 将其超类中派生最多的元类作为自己的元类,而不是 type。无需显式声明自定义元类。实际上,这就是需要元类而不仅仅是类装饰器的原因:这些装饰器只影响声明它们的类,对进一步的子类没有影响。
In [184]: class B(A): pass
Now at the meta-meta class
In [185]: B()
At the metaclass __call__
Out[185]: <__main__.B at 0x7fa6682ab3a0>
In [186]: B.__class__
Out[186]: __main__.EmptyMeta
即使在显式调用type 而不是class 语句中,派生类的元类也将是超类的元类。但是请注意,在这种情况下,我们将对“元数据”类的调用硬编码为type.__new__,而“元类的自定义元类”将被忽略:
In [187]: C = type("C", (A, ), {})
In [188]: C()
At the metaclass __call__
Out[188]: <__main__.C at 0x7fa653cb0d60>
如果您想以编程方式创建一个具有自定义“元元类”的类(上帝禁止任何人在学习目的之外需要它),types 模块中有一个特殊调用可以做到这一点:
In [192]: import types
In [193]: D = types.new_class("D", (A,), {})
Now at the meta-meta class
In [194]: D()
At the metaclass __call__
Out[194]: <__main__.D at 0x7fa6682959a0>
总结一下,如果一个类的超类有不同的元类,Python 将拒绝创建一个类。这在“现实世界”代码中有些常见,当人们尝试在一些带有 ORM 的框架中创建带有基类的抽象类(使用自定义元类)时,通常也有一个自定义元类:
In [203]: class Meta1(type): pass
In [204]: class Meta2(type): pass
In [205]: class A(metaclass=Meta1): pass
In [206]: class B(metaclass=Meta2): pass
In [207]: class C(A, B): pass
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-207-1def53cc27f4> in <module>
----> 1 class C(A, B): pass
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
这可以通过生成一个继承自的派生元类来解决
两个祖先分支中的元类(这要求两个元类
表现良好,使用super() 而不是硬编码调用type - 但是
维护良好且流行的框架就是这种情况):
In [208]: class Meta3(Meta1, Meta2): pass
In [209]: class C(A, B, metaclass=Meta3): pass
In [210]: