谁调用了元类

时间:2015-06-15 18:59:25

标签: python python-2.7 metaclass

这实际上源于对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中是不同的。

3 个答案:

答案 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)

执行类定义时,解释器会对元类进行“实例化”。