这实际上源于此处有关SO的讨论。
短版
def meta(name, bases, class_dict)
return type(name, bases, class_dict)
class Klass(object):
__metaclass__ = meta
当执行
meta()
类声明时,会调用Klass
。(python内部)代码的哪一部分实际调用
meta()
?较长版本
声明该类时,某些代码必须进行适当的属性检查,并查看是否在类型上声明了
__metaclass__
。如果存在,则必须使用众所周知的(class_name, bases, class_dict)
属性对该元类执行方法调用。对我来说,目前还不清楚是哪个代码负责该调用。我已经在CPython中进行了一些挖掘(请参见下文),但是我真的很想找到一个更明确的答案。
选项1:直接调用
元类调用被硬连接到类解析中。如果是这样,是否有任何证据呢?
选项2:由
type.__new__()
调用type_call()
中的代码调用 type_new()
,后者又调用 _PyType_CalculateMetaclass()
。这表明,在尝试查找从type()
返回的值时,实际上是在调用type()
的过程中完成了元类的解析。这将与“类”是“返回对象的可调用对象”的观点相一致。
选项3:有所不同
当然,我的所有猜测可能完全错误。
示例1:
class Meta(type):
pass
class A:
__metaclass__ = Meta
A.__class__ == Meta
这是
Meta.__new__()
返回的内容,因此这看起来是合法的。元类将自己放置为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:正确的初始版本,正确地从
Meta
导出type
。似乎还可以,但我不确定这是否达到了我的预期。另外:使它的行为类似于示例1的规范方法是什么?
编辑3:使用
type.__new__(...)
似乎可以按预期工作,这似乎也支持选项2。任何对内部python魔术有更深入了解的人都可以启发我吗?
编辑:A是关于元类的一个非常简洁的入门:http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/。还提供了一些非常不错的图表,引用资料,并重点介绍了python 2和3之间的区别。
编辑3:以下是Python 3的一个很好的答案。Python 3使用
__build_class__
创建一个类对象。但是,Python 2中的代码路径有所不同。 最佳答案
您可以相对容易地找到答案。首先,让我们找到构建类的操作码。
>>> 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);
因此,答案是元类由负责执行
BUILD_CLASS
操作码的函数解析和调用。