元类的 __new__ 是类创建的第一道关卡,接收 name、bases、namespace 三参数并返回未初始化类对象;__init__ 则用于后置检查,此时类结构已完整。

元类的 __new__ 是类对象诞生的第一道关卡
类定义语句执行时,Python 不是直接构造类对象,而是调用其指定的元类(默认是 type)的 __new__ 方法。这个方法接收类名、父类元组、属性字典三个核心参数,返回一个尚未初始化的类对象——此时 __init__ 还没运行,连类的 __dict__ 都未冻结。
常见错误是试图在元类 __new__ 中访问 cls.some_attr 或调用实例方法,这会报 AttributeError:因为类对象还没真正“活”起来。
- 务必用
name、bases、namespace三元组做判断和修改,比如过滤掉以_开头的属性名 - 若需注入方法,应直接写入
namespace字典,而非对返回的cls赋值 - 必须显式调用
super().__new__(...)并返回结果,漏掉这步会导致类创建失败
用 __init__ 做类级别的“后置检查”更安全
元类 __init__ 在 __new__ 返回类对象后立即执行,此时类已具备完整结构,可安全读取 cls.__dict__、遍历 cls.__mro__,甚至调用装饰器函数。
典型场景是强制接口实现:比如要求所有继承自 Task 的类必须定义 execute 方法。若缺失,__init__ 中抛出 TypeError 比运行时才发现更早、更明确。
- 不要在
__init__中修改cls.__dict__(它只读),要改属性请回到__new__操作namespace - 避免耗时操作,元类初始化发生在 import 时,慢了会拖累整个模块加载
- 注意继承链:子类元类未显式声明时,会沿用父类元类,
__init__会被逐层调用
自定义元类必须显式继承 type,不能直接用 class Meta:
这是新手最常踩的坑:class Meta: 定义的是普通类,不是元类;只有继承 type 才获得元类行为。Python 3.7+ 还要求元类必须是 type 的子类,否则 SyntaxError: metaclass conflict。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
另一个陷阱是多重继承下元类冲突。例如类同时继承 A(元类为 M1)和 B(元类为 M2),而 M1 和 M2 无继承关系,就会报错。
- 正确写法:
class MyMeta(type):,然后在目标类中用metaclass=MyMeta - 解决冲突:让
M2继承M1,或定义一个共同父元类 -
type本身是它的自己的元类,所以isinstance(type, type)为True——这点容易让人困惑,但不必深究,按规则写就行
比元类更轻量的替代方案:类装饰器和 __init_subclass__
90% 的元类使用场景其实可以用 __init_subclass__(Python 3.6+)代替。它在子类创建后自动触发,无需定义新元类,语法干净,调试友好。
比如统一注册所有子类到某个全局字典,用 __init_subclass__ 只需两行:
class Plugin:
plugins = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.plugins[cls.__name__] = cls
类装饰器则适合单次、非继承链通用的逻辑,比如自动添加日志或校验字段。它们不改变类创建机制本身,只是“事后加工”,出问题也更容易定位。
元类真正的不可替代场景极少:比如需要动态决定父类(根据配置选 BaseA 还是 BaseB)、拦截并重写所有方法名(如统一加前缀)、或与某些框架深度耦合(如 SQLAlchemy 的声明式映射)。别为了炫技而用元类。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










