这实际上源于对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()
期间实际完成了元类分辨率。
这与'" class"的概念一致。是" 可调用的,它返回一个对象"。
选项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。
任何对内部蟒蛇魔法有更深入了解的人都可以启发我吗?
编辑:A有关元类的简明入门:http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/。还有一些非常好的图表,参考文献,并且还强调了python 2和3之间的区别。
编辑3:Python 3下面有一个很好的答案.Python 3使用__build_class__
来创建一个类对象。代码路径 - 在Python 2中是不同的。
答案 0 :(得分:6)
您可以相对轻松地找到答案。首先,让我们找到构建类的操作码。
>>> 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
操作码的函数解析并调用。
答案 1 :(得分:2)
在Python 3中,在__build_class__
内置函数(调用它来处理class
语句)的代码中调用元类。此函数是Python 3中的新功能,Python 2中的等效C函数build_class
未在Python级别公开公开。但是,您可以在python/ceval.c
无论如何,这里是Python 3 __build_class__
实现中对元类对象的相关调用:
cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);
变量meta
是元类(type
或从参数或基类类型中找到的另一个元类。 margs
是一个元组,其位置参数为(name, bases, dct)
,而mkw
是一个字典,其中包含元类的关键字参数(仅限Python 3)。
Python 2代码执行类似的操作:
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
NULL);
答案 2 :(得分:-4)
执行类定义时,解释器会对元类进行“实例化”。