写 class Foo(Base): ... 的时候,大多数人不会去想这行代码背后发生了什么——它其实是一次真实的函数调用,调用的对象叫"元类"(metaclass)。这一篇本机真实拦截了这次调用,证明 class 语句就是语法糖;顺着真实源码扒出了这次调用完整的四步流程;最后用一个真实场景说明,为什么大多数时候你压根不需要碰这个东西。
Python 里"万物皆对象"这句话是认真的——包括类本身。
type(5) 是 int——这个大家都熟。
type(int) 是 type——类这个东西本身,也是某个"类的类"的实例,这个"类的类"就叫元类。
type(type) 还是 type——递归到这里打住,type 是自己的元类,是这条链的不动点。
print(type(5)) print(type(int)) print(type(type)) print(int.__class__ is type)<class 'int'> <class 'type'> <class 'type'> True
默认情况下,你写的每一个类,它的元类都是 type——元类这个概念一直都在起作用,只是平时感觉不到,因为大家都在用同一个默认的 type。metaclass= 做的事情,就是把这个默认值换成你自己写的东西。
自定义一个元类,重写 __new__/__init__,亲眼看看 class 语句执行时它们什么时候被调用——以及创建实例的时候它们还会不会再被调用一次。
class LoudMeta(type):
def __new__(mcs, name, bases, namespace, **kwargs):
print(f'LoudMeta.__new__ called: name={name!r}')
return super().__new__(mcs, name, bases, namespace, **kwargs)
def __init__(cls, name, bases, namespace, **kwargs):
print(f'LoudMeta.__init__ called on {cls}')
super().__init__(name, bases, namespace, **kwargs)
print('=== defining class Foo ===')
class Foo(metaclass=LoudMeta):
x = 1
print()
print('=== instantiating Foo() ===')
f = Foo()
=== defining class Foo ===
LoudMeta.__new__ called: name='Foo'
LoudMeta.__init__ called on <class '__main__.Foo'>
=== instantiating Foo() ===
LoudMeta.__new__/__init__ 在 class Foo 这行代码执行的瞬间就被调用了,不是等到有人写 Foo() 创建实例才触发——因为 class 语句本身就是在"创建 Foo 这个对象",而元类就是负责创建它的那个"构造函数"。后面 f = Foo() 完全没有再打印任何东西,因为那次调用的是 Foo 自己的 __new__/__init__(实例创建),和元类不是一回事。
class 语句编译后,真的就是在调用一个叫 __build_class__ 的内置函数——反汇编能直接看到。
import dis
code = compile('class Foo(Base):\n x = 1\n', '<test>', 'exec')
dis.dis(code)
LOAD_BUILD_CLASS
PUSH_NULL
LOAD_CONST (<code object Foo>)
MAKE_FUNCTION
LOAD_CONST ('Foo')
LOAD_NAME (Base)
CALL 3
STORE_NAME (Foo)
LOAD_BUILD_CLASS + CALL,传的三个参数是:类体编译成的函数、类名、基类——这就是 __build_class__(func, name, *bases, **kwds) 的真实调用。这个内置函数的 C 实现,做的事情正好是四步:
Python/bltinmodule.c · cpython @ v3.14.7, L144 /* 第一步:确定元类——显式 metaclass= 给了就用它;没给的话, * 没有基类就用 type,有基类就用第一个基类的类型 */ if (meta == NULL) { if (PyTuple_GET_SIZE(bases) == 0) { meta = (PyObject *) (&PyType_Type); } else { PyObject *base0 = PyTuple_GET_ITEM(bases, 0); meta = (PyObject *)Py_TYPE(base0); } } /* 再用 _PyType_CalculateMetaclass 在所有基类的元类之间找"最派生"的那个 */
Python/bltinmodule.c · cpython @ v3.14.7, L183 /* 第二步:ns = meta.__prepare__(name, bases) —— 拿到一个"容器", * 类体里的赋值语句接下来都会往这个容器里塞东西 */ ns = PyObject_VectorcallDict(prep, pargs, 2, mkw); ... /* 第三步:把类体代码在 ns 这个命名空间里跑一遍 —— * class 里每一条 "x = 1" 本质就是 ns['x'] = 1 */ cell = _PyEval_Vector(tstate, (PyFunctionObject *)func, ns, NULL, 0, NULL); ... /* 第四步:cls = meta(name, bases, ns) —— 真正把类对象造出来 */ cls = PyObject_VectorcallDict(meta, margs, 3, mkw);
这四步解释了元类到底能插手什么:第一步决定"用谁来造"(可以强行指定一个自定义元类);第二步决定"类体执行时用什么容器收集属性"(可以自定义,下面马上会演示这一步能做到什么程度);第四步是"最终拍板"这个类到底长什么样——注册、校验、批量修改属性,都发生在这一步。
继承多个基类,如果它们的元类互不相关,Python 没法决定该用哪个,会直接报错。
Objects/typeobject.c · cpython @ v3.14.7, L3921 /* Determine the most derived metatype. */ PyTypeObject * _PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) { winner = metatype; for (each base in bases) { tmptype = Py_TYPE(base); if (PyType_IsSubtype(winner, tmptype)) continue; if (PyType_IsSubtype(tmptype, winner)) { winner = tmptype; continue; } /* 谁都不是谁的子类型,判不出胜负 */ PyErr_SetString(PyExc_TypeError, "metaclass conflict: ..."); return NULL; } return winner; }
class MetaA(type): pass class MetaB(type): pass class Base1(metaclass=MetaA): pass class Base2(metaclass=MetaB): pass class Combined(Base1, Base2): passTypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
报错信息和源码里那句字面量一字不差。规则很直白:多个基类的元类之间,必须有一个是"最派生"的(是其他所有元类的子类),否则 Python 没法决定听谁的。
前面第二步提到 __prepare__ 能自定义"容器"——如果这个容器在写入时做校验,就能在类体刚执行到一半、类对象还不存在的时候拦截错误。
class NoDupeDict(dict):
def __setitem__(self, key, value):
if key in self and not key.startswith('__'):
raise TypeError(f'duplicate attribute {key!r}')
super().__setitem__(key, value)
class StrictMeta(type):
@classmethod
def __prepare__(mcs, name, bases, **kw):
return NoDupeDict()
class Bad(metaclass=StrictMeta):
x = 1
y = 2
x = 3 # 手滑写重了
TypeError: duplicate attribute 'x'
普通的类定义里,x = 1 后面再来一次 x = 3,Python 什么都不会说,后一个悄悄覆盖前一个——这是很多真实 bug 的来源(复制粘贴改名字漏了一处)。靠自定义 __prepare__ 返回的命名空间拦截重复赋值,是普通 Python 代码做不到的事,因为普通代码没有机会介入"类体执行期间"这个阶段。
"如果你不确定要不要用元类,那你不需要"——用一个最经典的元类使用场景(自动注册所有子类)实测对比。
# 写法一:元类
class PluginMeta(type):
registry = {}
def __new__(mcs, name, bases, ns):
cls = super().__new__(mcs, name, bases, ns)
if bases: PluginMeta.registry[name] = cls
return cls
class PluginBase(metaclass=PluginMeta): pass
class CSVPlugin(PluginBase): pass
print('元类写法:', PluginMeta.registry)
# 写法二:__init_subclass__(3.6+,专门为了减少对元类的需求而加的)
class PluginBase2:
registry = {}
def __init_subclass__(cls, **kw):
super().__init_subclass__(**kw)
PluginBase2.registry[cls.__name__] = cls
class XMLPlugin(PluginBase2): pass
print('__init_subclass__ 写法:', PluginBase2.registry)
print('PluginBase2 自己的元类还是:', type(PluginBase2) is type)
元类写法: {'CSVPlugin': <class '__main__.CSVPlugin'>}
__init_subclass__ 写法: {'XMLPlugin': <class '__main__.XMLPlugin'>}
PluginBase2 自己的元类还是: True
两种写法效果完全一样,但 __init_subclass__ 版本从头到尾没有定义任何元类——PluginBase2 的元类仍然是普通的 type。__init_subclass__(PEP 487,3.6 加入)和 __set_name__ 就是为了把"子类创建时想干点什么"这类最常见的需求,从元类里搬出来,变成普通类就能用的钩子——这也是 Tim Peters 那句话成立的真正原因:凡是能用这两个钩子解决的,就不必真的去写元类。真正必须用元类的场景,是要动第二步(__prepare__ 自定义命名空间)或者第一步(强行统一多个不相关基类的元类)这类 __init_subclass__ 够不着的地方。
复现前面"自动注册插件"的场景,包含一次真实会被拦截的重复赋值。
数据来自独立验证过的 Python 模拟,复现了真实四步流程(解析元类 → __prepare__ → 执行类体 → 构造类);第三个类故意重复赋值同一个属性,模拟里同样会在"执行类体"阶段被拦截,和真实实验的报错时机一致,过程经过独立回放交叉校验。