所以——这是一个有点混淆的问题,可以通过在交互模式下运行一些示例来回答,并简化。
但首先,在您陈述时:
type.__call__(...)会依次运行两个其他方法(__new__和__init__)。
这是一个简化了的过程。
当我们创建新类,比如解析一个类语句 class A:
,确实会调用 type.__call__
。但是这个调用在 Meta
的“类”中搜索。也就是说,“Meta”的“元类”——默认为type
。
请耐心听我解释:
当我们谈论没有自定义元类的普通类 E,E()
创建实例时,Python 会在 E
所属的类——也就是它的元类中搜索 __call__
方法。由于它是 type,因此会调用 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类的超类中的这些方法,其超类也是“type”类。
现在,回到你的问题:当从自定义元类的类继承时,例如你的类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]: