元类在类定义结束、实例尚未创建时触发,其__new__和__init__分别用于修改命名空间和验证类结构,是唯一能拦截并修改类构建过程的机制。

元类触发时机:类定义结束但实例尚未创建时
元类的 __new__ 和 __init__ 在 class 语句执行完、类对象刚生成时立即调用,此时类体已解析完毕,但任何实例都还没出现。这是唯一能拦截并修改类结构的时机——不是装饰器那种“事后补丁”,而是真正参与类构建。
常见错误是把元类当成类装饰器用:比如在 __init__ 里试图给未来实例加属性,结果发现实例根本没继承到;或者误以为元类能干预 __call__ 之外的方法调用——它只管“造类”,不管“造实例”后的逻辑。
-
__new__(mcs, name, bases, namespace):必须返回一个类对象,常用来重写namespace(如注入方法、改名字段) -
__init__(cls, name, bases, namespace):接收已创建好的类对象cls,适合做验证或轻量级修补(不能改__dict__中的不可变项) - 若同时定义两者,
__new__必须先返回类,__init__才会被调用;跳过__new__返回值会导致TypeError
动态注入方法:用 namespace 拦截并增强类定义
想让所有继承某元类的类自动带一个 dump() 方法?别在基类里硬写,直接在元类 __new__ 中往 namespace 字典里塞函数:
def dump(self):
return {k: v for k, v in self.__dict__.items() if not k.startswith('_')}
class AutoDumpMeta(type):
def __new__(mcs, name, bases, namespace):
namespace['dump'] = dump
return super().__new__(mcs, name, bases, namespace)
注意:namespace 是类体中顶层语句的符号表(不含继承来的属性),直接修改它比后期用 setattr(cls, ...) 更可靠——后者可能被 __slots__ 拦截,而前者在类构建前就生效。
- 避免覆盖用户已定义的同名方法:检查
'dump' not in namespace再注入 - 注入的函数若引用了
self,它就是普通实例方法,无需额外绑定 - 不要在
namespace中放复杂逻辑(如循环 import),元类执行时模块可能未完全加载
强制接口检查:在 __init__ 中验证类是否实现必需方法
想确保所有使用 ValidatedAPI 元类的类都实现了 handle() 和 schema()?在 __init__ 里查 cls 的 __dict__ 或用 hasattr:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
class ValidatedAPI(type):
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if not (hasattr(cls, 'handle') and callable(getattr(cls, 'handle'))):
raise TypeError(f"{name} must implement 'handle()' method")
if not hasattr(cls, 'schema'):
raise TypeError(f"{name} must define 'schema' attribute")
这里用 __init__ 而非 __new__,因为方法是否可调用、属性是否存在,需等类对象完整构建后才能准确判断;__new__ 阶段 namespace 还没合并父类内容,检查结果不可靠。
- 用
getattr(cls, 'xxx', None)替代直接访问,防止 AttributeError 干扰验证逻辑 - 若允许子类继承实现,检查时应排除抽象基类(
inspect.isabstract(cls)) - 报错信息里带上
name(类名),否则调试时不知道哪个类触发了失败
与 __init_subclass__ 的关键区别:谁负责、何时生效
Python 3.6+ 的 __init_subclass__ 看起来像元类简化版,但它只对**直接子类**生效,且调用时机晚于元类:__init_subclass__ 在子类创建完成后才触发,无法修改子类的 namespace 或阻止其创建。
例如:你想禁止某个基类被多重继承,元类能在 __new__ 中检查 len(bases) > 1 并直接 raise;而 __init_subclass__ 此时类已造好,只能事后警告,无法拦截。
- 需要修改类结构(增删方法、改
__slots__、动态设__annotations__)→ 选元类 - 只需对子类做轻量初始化(如注册到全局表、打日志)→ 优先用
__init_subclass__,更简洁易读 - 两者混用时,元类的
__new__/__init__一定先执行,__init_subclass__最后调用
元类真正的复杂点不在语法,而在责任边界——它介入的是 Python 类模型最底层的构造过程,一旦出错,往往表现为类导入失败或 TypeError: metaclass conflict,这类问题很难通过常规调试定位,得从 type() 调用链反推。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










