谁叫了元类

dhk*_*hke 11 python metaclass python-2.7

这实际上源于对SO的讨论.

精简版

def meta(name, bases, class_dict)
    return type(name, bases, class_dict)

class Klass(object):
    __metaclass__ = meta
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这就是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
Run Code Online (Sandbox Code Playgroud)

编辑2:正确的初始版本,正确派生Metatype.

似乎没问题,但我不确定这是否符合我的想法.另外:使其行为与示例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中是不同的.

Dun*_*nes 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    
Run Code Online (Sandbox Code Playgroud)

所以操作码是BUILD_CLASS.现在让我们搜索该术语的源代码(在github镜像上轻松完成).

你会得到几个结果,但最有趣的是Python/ceval.c声明函数static PyObject * build_class(PyObject *, PyObject *, PyObject *);并有一个case语句BUILD_CLASS.搜索文件,你可以找到build_class从第4430行开始的函数定义.在第4456行,我们找到你要查找的代码:

result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
                      NULL);
Run Code Online (Sandbox Code Playgroud)

所以答案是元类由负责执行BUILD_CLASS操作码的函数解析并调用.