这实际上源于此处有关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操作码的函数解析和调用。

10-08 11:14
查看更多