谁调用元类
Who calls the metaclass
这实际上源于对 SO 的讨论。
短版
def meta(name, bases, class_dict)
return type(name, bases, class_dict)
class Klass(object):
__metaclass__ = meta
meta()
在执行 Klass
class 声明时被调用。
(python 内部)代码的哪一部分实际调用了 meta()
?
长版
当声明 class 时,某些代码必须进行适当的属性检查并查看是否在类型上声明了 __metaclass__
。如果存在,它必须使用众所周知的 (class_name, bases, class_dict)
属性对该元 class 执行方法调用。我不太清楚哪个代码负责该调用。
我在 CPython 中做了一些挖掘(见下文),但我真的很想得到更接近确定答案的东西。
方式一:直接调用
metaclass 调用硬连接到 class 解析中。如果是这样,有这方面的证据吗?
选项2:由type.__new__()
调用
type_call()
calls type_new()
which in turn calls _PyType_CalculateMetaclass()
中的代码。这表明当试图从 type()
中找出 return 的值时,元 class 解析实际上是在调用 type()
期间完成的
这符合 "class" 是“可调用对象return是一个对象”的概念。
选项 3:有所不同
当然,我所有的猜测都可能完全错误。
Some example cases that we came up with in chat:
示例 1:
class Meta(type):
pass
class A:
__metaclass__ = Meta
A.__class__ == Meta
这就是 Meta.__new__()
return 的内容,因此这似乎是合法的。 metaclass 将自己设为 A.__class__
示例 2:
class Meta(type):
def __new__(cls, class_name, bases, class_dict):
return type(class_name, bases, class_dict)
class A(object):
__metaclass__ = Meta
A.__class__ == type
编辑 2:正确的初始版本,从 type
.
正确派生 Meta
看起来还不错,但我不确定这是否符合我的预期。另外:使它像示例 1 中那样运行的规范方法是什么?
编辑 3:使用 type.__new__(...)
似乎按预期工作,这似乎也支持选项 2。
谁能对内部 python 魔法有更深入的了解?
编辑:A 关于 metaclasses 的相当简洁的入门:http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/。还有一些非常好的图表、参考资料,还强调了 python 2 和 3 之间的差异。
编辑 3:Python 3 下面有一个很好的答案。Python 3 使用 __build_class__
创建一个 class 对象。 Python 2.
中的代码路径 -- 然而-- 不同
当执行 class 定义时,class 元 class 是解释器 "instantiated"。
在Python 3、__build_class__
builtin function (which is called to handle class
statements). This function is new in Python 3, and the equivalent C function build_class
in Python 2 is not publicly exposed at the Python level. You can however find the source in python/ceval.c
的代码中调用了metaclass
无论如何,这是 Python 3 __build_class__
实现中对 metaclass 对象的相关调用:
cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);
变量 meta
是元class(type
或从参数或基类型[=35= 中找到的另一个元class ]). margs
是一个带有位置参数 (name, bases, dct)
的元组,mkw
是一个字典,带有 metaclass 的关键字参数(只有 Python 3 的东西)。
Python 2 代码做了类似的事情:
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
NULL);
您可以相对轻松地找到答案。首先,让我们找到构建 class.
的操作码
>>> def f():
class A(object):
__metaclass__ = type
>>> import dis
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('A')
3 LOAD_GLOBAL 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 2 (<code object A at 0000000001EBDA30, file "<pyshell#3>", line 2>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_FAST 0 (A)
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
所以操作码是BUILD_CLASS
。现在让我们搜索该术语的来源(在 github mirror 上很容易完成)。
您会得到几个结果,但其中最有趣的是 Python/ceval.c
,它声明了函数 static PyObject * build_class(PyObject *, PyObject *, PyObject *);
并且有一个 BUILD_CLASS
的 case 语句。搜索文件,您可以找到从第 4430 行开始的 build_class
的函数定义。在第 4456 行,我们找到您要查找的代码:
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
NULL);
所以答案是 metaclass 由负责执行 BUILD_CLASS
操作码的函数解析和调用。
这实际上源于对 SO 的讨论。
短版
def meta(name, bases, class_dict)
return type(name, bases, class_dict)
class Klass(object):
__metaclass__ = meta
meta()
在执行 Klass
class 声明时被调用。
(python 内部)代码的哪一部分实际调用了 meta()
?
长版
当声明 class 时,某些代码必须进行适当的属性检查并查看是否在类型上声明了 __metaclass__
。如果存在,它必须使用众所周知的 (class_name, bases, class_dict)
属性对该元 class 执行方法调用。我不太清楚哪个代码负责该调用。
我在 CPython 中做了一些挖掘(见下文),但我真的很想得到更接近确定答案的东西。
方式一:直接调用
metaclass 调用硬连接到 class 解析中。如果是这样,有这方面的证据吗?
选项2:由type.__new__()
type_call()
calls type_new()
which in turn calls _PyType_CalculateMetaclass()
中的代码。这表明当试图从 type()
type()
期间完成的
这符合 "class" 是“可调用对象return是一个对象”的概念。
选项 3:有所不同
当然,我所有的猜测都可能完全错误。
Some example cases that we came up with in chat:
示例 1:
class Meta(type):
pass
class A:
__metaclass__ = Meta
A.__class__ == Meta
这就是 Meta.__new__()
return 的内容,因此这似乎是合法的。 metaclass 将自己设为 A.__class__
示例 2:
class Meta(type):
def __new__(cls, class_name, bases, class_dict):
return type(class_name, bases, class_dict)
class A(object):
__metaclass__ = Meta
A.__class__ == type
编辑 2:正确的初始版本,从 type
.
Meta
看起来还不错,但我不确定这是否符合我的预期。另外:使它像示例 1 中那样运行的规范方法是什么?
编辑 3:使用 type.__new__(...)
似乎按预期工作,这似乎也支持选项 2。
谁能对内部 python 魔法有更深入的了解?
编辑:A 关于 metaclasses 的相当简洁的入门:http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/。还有一些非常好的图表、参考资料,还强调了 python 2 和 3 之间的差异。
编辑 3:Python 3 下面有一个很好的答案。Python 3 使用 __build_class__
创建一个 class 对象。 Python 2.
当执行 class 定义时,class 元 class 是解释器 "instantiated"。
在Python 3、__build_class__
builtin function (which is called to handle class
statements). This function is new in Python 3, and the equivalent C function build_class
in Python 2 is not publicly exposed at the Python level. You can however find the source in python/ceval.c
无论如何,这是 Python 3 __build_class__
实现中对 metaclass 对象的相关调用:
cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);
变量 meta
是元class(type
或从参数或基类型[=35= 中找到的另一个元class ]). margs
是一个带有位置参数 (name, bases, dct)
的元组,mkw
是一个字典,带有 metaclass 的关键字参数(只有 Python 3 的东西)。
Python 2 代码做了类似的事情:
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
NULL);
您可以相对轻松地找到答案。首先,让我们找到构建 class.
的操作码>>> def f():
class A(object):
__metaclass__ = type
>>> import dis
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('A')
3 LOAD_GLOBAL 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 2 (<code object A at 0000000001EBDA30, file "<pyshell#3>", line 2>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_FAST 0 (A)
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
所以操作码是BUILD_CLASS
。现在让我们搜索该术语的来源(在 github mirror 上很容易完成)。
您会得到几个结果,但其中最有趣的是 Python/ceval.c
,它声明了函数 static PyObject * build_class(PyObject *, PyObject *, PyObject *);
并且有一个 BUILD_CLASS
的 case 语句。搜索文件,您可以找到从第 4430 行开始的 build_class
的函数定义。在第 4456 行,我们找到您要查找的代码:
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
NULL);
所以答案是 metaclass 由负责执行 BUILD_CLASS
操作码的函数解析和调用。