The call order of python3 metaclass
当我试图理解
我输入以下代码来验证它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class Meta(type): def __call__(self): print("Meta __call__") super(Meta, self).__call__() def __new__(mcs, name, bases, attrs, **kwargs): print("Meta __new__") return super().__new__(mcs, name, bases, kwargs) def __prepare__(msc, name, **kwargs): print("Meta __prepare__") return {} class SubMeta(Meta): def __call__(self): print("SubMeta __call__!") super().__call__() def __new__(mcs, name, bases, attrs, **kwargs): print("SubMeta __new__") return super().__new__(mcs, name, bases, kwargs) def __prepare__(msc, name, **kwargs): print("SubMeta __prepare__") return Meta.__prepare__(name, kwargs) class B(metaclass = SubMeta): pass b = B() |
然而,结果似乎不像下面这样。
1 2 3 4 5 6 | SubMeta __prepare__ Meta __prepare__ SubMeta __new__ Meta __new__ SubMeta __call__! Meta __call__ |
如有任何帮助,我们将不胜感激。
发现的诡计
更新2:根据行为,下面调用
为了定义一个具有元类的类,我们通常称元类的
1 2 3 | print("call Meta") print("Meta returns:", Meta('name', (), {})) print("finished calling Meta") |
生产:好的。
1 2 3 4 5 6 | call Meta M0 __call__: mmcls=<class '__main__.Meta'>, args=('name', (), {}), kwargs={} Meta __new__: mcs=<class '__main__.Meta'>, name='name', bases=(), attrs={}, kwargs={} Meta __init__: mcs=<class '__main__.name'>, name='name', bases=(), attrs={}, kwargs={} Meta returns: <class '__main__.name'> finished calling Meta |
换句话说,我们看到
1 | print("type(Meta) =", type(Meta)) |
印刷品:好的。
1 | type(Meta) = <class '__main__.M0'> |
这就解释了这是从哪里来的。(我仍然认为应该在文档中强调这一点,文档中还应该描述对元类类型的约束,这些约束在
我不知道您的图表来自何处,但它是错误的。update:您实际上可以为您的元类创建一个元类;请参阅jsbueno的答案,我已经更新了下面的示例。新的句子/文本是粗体的,除了最后一节描述我对明显缺乏文档的困惑。好的。
现有的元类代码至少有一个错误。最重要的是,它的
克里斯的回答包含正确的定义。但元类方法参数和类方法参数之间存在一些不幸的不对称性,我将在下面说明。好的。
还有一件事可能会有所帮助:请注意,在创建类EDOCX1的任何实例(25)之前调用元类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class M0(type): def __call__(mmcls, *args, **kwargs): print("M0 __call__: mmcls={!r}," "args={!r}, kwargs={!r}".format(mmcls, args, kwargs)) return super().__call__(*args, **kwargs) class Meta(type, metaclass=M0): def __call__(cls, *args, **kwargs): print("Meta __call__: cls={!r}," "args={!r}, kwargs={!r}".format(cls, args, kwargs)) return super().__call__(*args, **kwargs) def __new__(mcs, name, bases, attrs, **kwargs): print("Meta __new__: mcs={!r}, name={!r}, bases={!r}," "attrs={!r}, kwargs={!r}".format(mcs, name, bases, attrs, kwargs)) return super().__new__(mcs, name, bases, attrs) def __init__(mcs, name, bases, attrs, **kwargs): print("Meta __init__: mcs={!r}, name={!r}, bases={!r}," "attrs={!r}, kwargs={!r}".format(mcs, name, bases, attrs, kwargs)) super().__init__(name, bases, attrs, **kwargs) @classmethod def __prepare__(cls, name, bases, **kwargs): print("Meta __prepare__: name={!r}," "bases={!r}, kwargs={!r}".format(name, bases, kwargs)) return {} print("about to create class A") class A(metaclass=Meta): pass print("finished creating class A") print("about to create class B") class B(A, metaclass=Meta, foo=3): @staticmethod def __new__(cls, *args, **kwargs): print("B __new__: cls={!r}," "args={!r}, kwargs={!r}".format(cls, args, kwargs)) return super().__new__(cls) def __init__(self, *args, **kwargs): print("B __init__: args={!r}, kwargs={!r},".format(args, kwargs)) print("finished creating class B") print("about to create instance b = B()") b = B('hello', bar=7) print("finished creating instance b") |
现在,让我们观察一下当我运行这个程序时会发生什么,并将每个部分分开:好的。
1 2 3 4 5 6 7 | $ python3.6 meta.py about to create class A Meta __prepare__: name='A', bases=(), kwargs={} M0 __call__: mmcls=<class '__main__.Meta'>, args=('A', (), {'__module__': '__main__', '__qualname__': 'A'}), kwargs={} Meta __new__: mcs=<class '__main__.Meta'>, name='A', bases=(), attrs={'__module__': '__main__', '__qualname__': 'A'}, kwargs={} Meta __init__: mcs=<class '__main__.A'>, name='A', bases=(), attrs={'__module__': '__main__', '__qualname__': 'A'}, kwargs={} finished creating class A |
为了创建类
(我不在这里打印
接下来,在从
由于类
1 2 3 4 5 6 | about to create class B Meta __prepare__: name='B', bases=(<class '__main__.A'>,), kwargs={'foo': 3} M0 __call__: mmcls=<class '__main__.Meta'>, args=('B', (<class '__main__.A'>,), {'__module__': '__main__', '__qualname__': 'B', '__new__': <staticmethod object at 0x800ad0a58>, '__init__': <function B.__init__ at 0x800ad2840>, '__classcell__': <cell at 0x800a749d8: empty>}), kwargs={'foo': 3} Meta __new__: mcs=<class '__main__.Meta'>, name='B', bases=(<class '__main__.A'>,), attrs={'__module__': '__main__', '__qualname__': 'B', '__new__': <staticmethod object at 0x800ad0940>, '__init__': <function B.__init__ at 0x800ad27b8>, '__classcell__': <cell at 0x800a745b8: empty>}, kwargs={'foo': 3} Meta __init__: mcs=<class '__main__.B'>, name='B', bases=(<class '__main__.A'>,), attrs={'__module__': '__main__', '__qualname__': 'B', '__new__': <staticmethod object at 0x800ad0940>, '__init__': <function B.__init__ at 0x800ad27b8>, '__classcell__': <cell at 0x800a745b8: Meta object at 0x802047018>}, kwargs={'foo': 3} finished creating class B |
这个比较有趣。现在我们有一个基类,即
关键字参数传递给所有三个元类方法,加上元类的方法。(我不太清楚为什么。请注意,在任何元类方法中修改字典都不会对其他方法产生影响,因为它每次都是原始关键字参数的副本。但是,我们可以在元类中对其进行修改:将
现在我们有了类
1 2 | about to create instance b = B() Meta __call__: cls=<class '__main__.B'>, args=('hello',), kwargs={'bar': 7} |
有可能改变通过的
1 2 3 | B __new__: cls=<class '__main__.B'>, args=('hello',), kwargs={'bar': 7} B __init__: args=('hello',), kwargs={'bar': 7}, finished creating instance b |
它完成了类
注意,
1 | return super().__new__(cls) |
所以我们调用
1 2 3 4 | def __call__(cls, *args, **kwargs): print("Meta __call__: cls={!r}," "args={!r}, kwargs={!r}".format(cls, args, kwargs)) return object.__new__(cls) |
我们将看到,
1 2 3 | about to create instance b = B() Meta __call__: cls=<class '__main__.B'>, args=('hello',), kwargs={'bar': 7} finished creating instance b |
实际上,这将创建一个无用/未初始化的实例
引用第3.3.3.6节:好的。
Once the class namespace has been populated by executing the class body, the class object is created by calling
metaclass(name, bases, namespace, **kwds) (the additional keywords passed here are the same as those passed to__prepare__ ).Ok.
这就解释了在创建
下一段提到了
在前面的第3.3.3.3到3.3.3.5节中,文档描述了确定元类、准备类名称空间和执行类主体的过程。这是应该描述元类操作的地方,但不是。好的。
几个附加的部分描述了一些附加的约束。一个重要的例子是3.3.10,它讨论了如何通过对象类型找到特殊方法,绕过常规成员属性查找,甚至(有时)绕过元类getattribute,说:好的。
Bypassing the
__getattribute__() machinery in this fashion provides significant scope for speed optimisations within the interpreter, at the cost of some flexibility in the handling of special methods (the special method must be set on the class object itself in order to be consistently invoked by the interpreter).Ok.
更新2:这确实是技巧的秘密:通过类型的类型找到特殊的
尽管@torek的回答很冗长,而且还有很多关于类创建的其他细节,但您对这个问题的总结基本上是正确的。
您的代码中唯一的错误,使您感到困惑的是,您称之为
只需将
1 2 | class SubMeta(type, metaclass=Meta): ... |
(它也不需要从"meta"继承——它只能从
这是我刚刚在终端输入的另一个较短的示例。抱歉,命名不一致,而且不太完整,但它显示了要点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class M(type): def __call__(mmcls, *args, **kwargs): print("M's call", args, kwargs) return super().__call__(*args, **kwargs) class MM(type, metaclass=M): def __prepare__(cls, *args, **kw): print("MM Prepare") return {} def __new__(mcls, *args, **kw): print("MM __new__") return super().__new__(mcls, *args, **kw) class klass(metaclass=MM): pass |
在处理
1 2 3 | MM Prepare M's call ('klass', (), {'__module__': '__main__', '__qualname__': 'klass'}) {} MM __new__ |
此外
如您所见,使用元元类,可以自定义元类
- 不能控制对
__prepare__ 的呼叫 - 无法控制对所创建类的
__init_subclass__ 的调用 - 可以控制何时调用描述符的
__set_name__ 。
最后两项发生在meta meta的