Python 元类中的继承是如何工作的?
How does inheritance work in Python metaclass?
假设,我有一个自定义元class和一个链接到它的class:
class Meta(type): pass
class A(metaclass=Meta): pass
据我了解,在class A
语句的末尾,执行了以下步骤:
- 呼叫
Meta('A', (), {})
.
- 因为第1步是内置调用,也就是说
type.__call__(...)
会被调用。这是因为 type
链接到 Meta.__class__
.
type.__call__(...)
依次运行另外两个方法(一个__new__
和一个__init__
)。
- 如果
Meta
定义了其中一个或两个方法,那么在 type.__call__
内部,这些方法将被调用为 Meta.__new__(...)
and/or Meta.__init__(...)
.
- class
A
已创建并链接到 Meta
(A.__class__
).
现在,假设我有 A
的子class:
class Meta(type): pass
class A(metaclass=Meta): pass
class B(A): pass
在class B
语句的末尾,以下步骤是否正确?
- 调用
type('B', (), {})
而不是 Meta
,因为 B.__class__
是 type
。
- 调用
type.__call__(...)
进而 运行 其他两种方法(__new__
和 __init__
)。
type.__new__(type, 'B', (A,), {})
.
type.__init__(cls, 'B', (A,), {})
.
假设上述步骤是正确的(我对此表示怀疑),难道 B.__class__
不应该给出 type
而不是 Meta
吗?我的推理是 B
是使用默认 type
metaclass 创建的。但是打印出 B.__class__
给出 Meta
而不是 type
.
print(B.__class__) #<class '__main__.Meta'>
此外,如果我手动创建一个 class 并将 A
作为父级,创建的 class 将再次链接到 Meta
.
C = type.__call__(type, 'C', (A,), {})
print(C.__class__) #<class '__main__.Meta'>
#or
D = type.__new__(type, 'D', (A,), {})
print(D.__class__) #<class '__main__.Meta'>
我的问题是 Python 如何创建 class B/C
以及 B/C
如何链接到 Meta
?
Call type('B', (), {}) instead of Meta, because B.class is type.
正如您稍后指出的那样,它不是。
>>> class Meta(type): pass
...
>>> class A(metaclass=Meta): pass
...
>>> class B(A): pass
...
>>> type(B)
<class '__main__.Meta'>
>>>
My question is how Python create the class B/C and how B/C is linked to the Meta?
如果X
class继承了Y
class,则X
的metaclass与meta[=相同Y
的 25=]。您可以在 data model documentation.
上找到详细信息
来自文档:
The appropriate metaclass for a class definition is determined as
follows:
if no bases and no explicit metaclass are given, then type() is used;
if an explicit metaclass is given and it is not an instance of type(),
then it is used directly as the metaclass;
if an instance of type() is given as the explicit metaclass, or bases
are defined, then the most derived metaclass is used.
The most derived metaclass is selected from the explicitly specified
metaclass (if any) and the metaclasses (i.e. type(cls)) of all
specified base classes. The most derived metaclass is one which is a
subtype of all of these candidate metaclasses. If none of the
candidate metaclasses meets that criterion, then the class definition
will fail with TypeError.
所以 --- 一个可以回答的有点令人困惑的问题,并且简化了一些
通过简单地 运行 交互模式中的一些示例。
但首先,当您声明:
type.__call__(...) in turn run two other methods (a __new__ and a __init__).
这是对发生的事情的简化。
当我们创建新的 class 时,就像解析 class 语句 class A:
一样,type.__call__
会被调用。但是这个调用是在Meta
本身的class中搜索的。也就是说,“Meta”的“metaclass”——默认情况下是 type
.
请耐心等待:
当我们谈论没有自定义元 class 的普通 class E 时,您通过执行 E()
- Python 搜索 __call__
方法来创建实例在 class 其中 E
是一个实例:即它的 metaclass。因为它是类型,所以 type.__call__
被调用。正如您所说,它是 type.__call__
调用 __new__
和 __init__
方法,但不仅限于 metaclasses:它在任何对象实例化中编排这些调用 - 确切Python 中的任何对象实例化都使用相同的机制:普通对象和 classes:
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>
所以,简而言之:当创建一个classA,它是Meta的一个实例时,会调用Meta的class的__call__
方法。这将在 metaclass Meta 中调用 __init__
和 __new__
。如果那些没有定义,普通的属性查找会在Meta的superclass中调用这些方法,这恰好also是"type"。
现在,继续您的问题:当一个人从 class 继承自具有自定义元 class 时,例如您的 B
class、Python 将其超class 中最派生的元class 作为自己的元class,而不是type
。无需显式声明自定义元class。实际上,这就是需要 metaclass 而不仅仅是 Class 装饰器的原因:它们只影响声明它们的 class ,并且对进一步的 sub[=70 没有影响=]es.
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
语句,派生的 class' metaclass 也将是 metaclass超级class。但是请注意,在这种情况下,我们将对“metameta”class 的调用硬编码为 type.__new__
,并且忽略了“metaclass 的自定义元 class” :
In [187]: C = type("C", (A, ), {})
In [188]: C()
At the metaclass __call__
Out[188]: <__main__.C at 0x7fa653cb0d60>
如果您想以编程方式创建一个具有自定义“元元 class”的 class(除了学习目的,上帝禁止任何人需要它),在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>
总结一下,请注意,如果 class 的超 class 有不同的元 class,Python 将拒绝创建 class 完全没有。这在“现实世界”代码中有些常见,当人们尝试在带有 ORM 的某些框架中创建带有基础 class 的抽象 classes(使用自定义元 class)时,这通常还有自定义元class:
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
可以通过生成继承自的派生元 class 来修复
两个祖先分支中的 metaclasses(这需要两个 metaclasses
表现良好,使用 super()
而不是对 type
的硬编码调用 - 但是
维护良好且流行的框架就是这种情况):
In [208]: class Meta3(Meta1, Meta2): pass
In [209]: class C(A, B, metaclass=Meta3): pass
In [210]:
假设,我有一个自定义元class和一个链接到它的class:
class Meta(type): pass
class A(metaclass=Meta): pass
据我了解,在class A
语句的末尾,执行了以下步骤:
- 呼叫
Meta('A', (), {})
. - 因为第1步是内置调用,也就是说
type.__call__(...)
会被调用。这是因为type
链接到Meta.__class__
. type.__call__(...)
依次运行另外两个方法(一个__new__
和一个__init__
)。- 如果
Meta
定义了其中一个或两个方法,那么在type.__call__
内部,这些方法将被调用为Meta.__new__(...)
and/orMeta.__init__(...)
. - class
A
已创建并链接到Meta
(A.__class__
).
现在,假设我有 A
的子class:
class Meta(type): pass
class A(metaclass=Meta): pass
class B(A): pass
在class B
语句的末尾,以下步骤是否正确?
- 调用
type('B', (), {})
而不是Meta
,因为B.__class__
是type
。 - 调用
type.__call__(...)
进而 运行 其他两种方法(__new__
和__init__
)。 type.__new__(type, 'B', (A,), {})
.type.__init__(cls, 'B', (A,), {})
.
假设上述步骤是正确的(我对此表示怀疑),难道 B.__class__
不应该给出 type
而不是 Meta
吗?我的推理是 B
是使用默认 type
metaclass 创建的。但是打印出 B.__class__
给出 Meta
而不是 type
.
print(B.__class__) #<class '__main__.Meta'>
此外,如果我手动创建一个 class 并将 A
作为父级,创建的 class 将再次链接到 Meta
.
C = type.__call__(type, 'C', (A,), {})
print(C.__class__) #<class '__main__.Meta'>
#or
D = type.__new__(type, 'D', (A,), {})
print(D.__class__) #<class '__main__.Meta'>
我的问题是 Python 如何创建 class B/C
以及 B/C
如何链接到 Meta
?
Call type('B', (), {}) instead of Meta, because B.class is type.
正如您稍后指出的那样,它不是。
>>> class Meta(type): pass
...
>>> class A(metaclass=Meta): pass
...
>>> class B(A): pass
...
>>> type(B)
<class '__main__.Meta'>
>>>
My question is how Python create the class B/C and how B/C is linked to the Meta?
如果X
class继承了Y
class,则X
的metaclass与meta[=相同Y
的 25=]。您可以在 data model documentation.
来自文档:
The appropriate metaclass for a class definition is determined as follows:
if no bases and no explicit metaclass are given, then type() is used;
if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass;
if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used.
The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls)) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses. If none of the candidate metaclasses meets that criterion, then the class definition will fail with TypeError.
所以 --- 一个可以回答的有点令人困惑的问题,并且简化了一些 通过简单地 运行 交互模式中的一些示例。
但首先,当您声明:
type.__call__(...) in turn run two other methods (a __new__ and a __init__).
这是对发生的事情的简化。
当我们创建新的 class 时,就像解析 class 语句 class A:
一样,type.__call__
会被调用。但是这个调用是在Meta
本身的class中搜索的。也就是说,“Meta”的“metaclass”——默认情况下是 type
.
请耐心等待:
当我们谈论没有自定义元 class 的普通 class E 时,您通过执行 E()
- Python 搜索 __call__
方法来创建实例在 class 其中 E
是一个实例:即它的 metaclass。因为它是类型,所以 type.__call__
被调用。正如您所说,它是 type.__call__
调用 __new__
和 __init__
方法,但不仅限于 metaclasses:它在任何对象实例化中编排这些调用 - 确切Python 中的任何对象实例化都使用相同的机制:普通对象和 classes:
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>
所以,简而言之:当创建一个classA,它是Meta的一个实例时,会调用Meta的class的__call__
方法。这将在 metaclass Meta 中调用 __init__
和 __new__
。如果那些没有定义,普通的属性查找会在Meta的superclass中调用这些方法,这恰好also是"type"。
现在,继续您的问题:当一个人从 class 继承自具有自定义元 class 时,例如您的 B
class、Python 将其超class 中最派生的元class 作为自己的元class,而不是type
。无需显式声明自定义元class。实际上,这就是需要 metaclass 而不仅仅是 Class 装饰器的原因:它们只影响声明它们的 class ,并且对进一步的 sub[=70 没有影响=]es.
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
语句,派生的 class' metaclass 也将是 metaclass超级class。但是请注意,在这种情况下,我们将对“metameta”class 的调用硬编码为 type.__new__
,并且忽略了“metaclass 的自定义元 class” :
In [187]: C = type("C", (A, ), {})
In [188]: C()
At the metaclass __call__
Out[188]: <__main__.C at 0x7fa653cb0d60>
如果您想以编程方式创建一个具有自定义“元元 class”的 class(除了学习目的,上帝禁止任何人需要它),在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>
总结一下,请注意,如果 class 的超 class 有不同的元 class,Python 将拒绝创建 class 完全没有。这在“现实世界”代码中有些常见,当人们尝试在带有 ORM 的某些框架中创建带有基础 class 的抽象 classes(使用自定义元 class)时,这通常还有自定义元class:
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
可以通过生成继承自的派生元 class 来修复
两个祖先分支中的 metaclasses(这需要两个 metaclasses
表现良好,使用 super()
而不是对 type
的硬编码调用 - 但是
维护良好且流行的框架就是这种情况):
In [208]: class Meta3(Meta1, Meta2): pass
In [209]: class C(A, B, metaclass=Meta3): pass
In [210]: