2025-10-06 Python不基础 15——metaclass
文章目录
- 1. `type`
- 2. 元类定义与使用
- 2.1. 定义元类(继承自`type`)
- 2.2. 指定目标类使用元类(`metaclass`参数)
- 2.3. “继承元类” vs “指定元类”
- 3. 元类核心方法
- 3.1. `__new__`
- 3.2. `__init__`
- 3.3. `__call__`
- 4. 应用场景
- 5. `super`调用
- 6. 总结
本文参考视频链接:
- https://www.bilibili.com/video/BV13F411g7CD
元类(Metaclass)是“创建类的类”,用于自定义类的创建流程。
要理解元类,必须先回顾Python中“类的默认创建方式”——所有普通类(如class A:
)的底层都是由type
(Python内置元类)创建的。元类的本质就是替换默认的type
,自定义类的创建逻辑。
1. type
type
是Python的“内置元类”,所有普通类都是type
的实例(即“类是元类的对象”)。
静态定义类(class A:
)与动态调用type
创建类完全等价,type
是默认的“类的创建者”(即内置元类)。
# 1. 静态定义类A
class A:name = "AAA"def F(self):print(1)# 2. 动态调用type创建类A(与上述静态定义完全等价)
def F(self):print(1)
A = type("A", # 类名(字符串)(), # 父类元组(无父类时为空){"name": "AAA", "F": F} # 类属性/方法字典
)
元类的核心是“自定义类的创建逻辑”——当我们不想用默认的type
创建类时,可以定义一个继承自type
的类(即元类),并指定目标类由这个元类创建。
元类的本质关系:实例 → 类 → 元类
Python中存在三层层级关系,这是理解元类的关键:
层级 | 角色 | 示例 | 关系描述 |
---|---|---|---|
第1层 | 实例(Object) | o = A() 中的o | o 是A (类)的实例 |
第2层 | 类(Class) | class A: 中的A | A 是M (元类)的实例 |
第3层 | 元类(Metaclass) | class M(type): 中的M | M 继承自type ,是“创建类的类” |
小结:
- 实例由类创建(
o = A()
); - 类由元类创建(
A = M(...)
,默认M
是type
)。
2. 元类定义与使用
2.1. 定义元类(继承自type
)
元类必须继承自type
(因为type
是所有元类的“基类”),通过重载type
的方法(如__new__
、__init__
)实现自定义逻辑。
# 定义元类M,继承自type
class M(type):# 重载__new__方法:在类创建前执行,负责创建类对象def __new__(cls, name, bases, attrs):# cls:元类本身(即M)# name:目标类的类名(如"A")# bases:目标类的父类元组(如())# attrs:目标类的属性/方法字典(如{"name": "AAA", "F": F})print(f"类名:{name}")print(f"父类:{bases}")print(f"类属性:{attrs}")# 调用父类(type)的__new__,最终创建并返回类对象return super().__new__(cls, name, bases, attrs)
2.2. 指定目标类使用元类(metaclass
参数)
通过在类定义时添加metaclass
参数,指定该类由自定义元类(如M
)创建,而非默认的type
。
# 定义类A,指定元类为M(而非默认的type)
# 注意:不是“A继承M”,而是“A由M创建”
class A(metaclass=M):name = "AAA"def F(self):print(1)
运行上述代码时,无需创建A的实例,就会打印以下内容:
类名:A
父类:()
类属性:{'__module__': '__main__', '__qualname__': 'A', 'name': 'AAA', 'F': <function A.F at 0x...>}
结论:元类的__new__
方法在目标类定义时自动调用(而非实例化时),负责创建类对象。
2.3. “继承元类” vs “指定元类”
“类继承元类”和“类指定元类”两者本质不同:
写法 | 含义 | 结果 |
---|---|---|
class A(M): | A继承M(M是普通类) | A是M的子类,A的元类仍是默认的type |
class A(metaclass=M): | A由M创建(M是元类,继承自type) | A是M的实例,A的元类是M |
验证方式:查看类的元类(type(A)
)
class M(type):passclass A(metaclass=M):passclass B(M): # B继承M(M是元类,但B的元类仍是type)passprint(type(A)) # 输出:<class '__main__.M'>(A的元类是M)
print(type(B)) # 输出:<class 'type'>(B的元类是默认的type)
3. 元类核心方法
3.1. __new__
- 调用时机:目标类(如A)定义时,元类首先调用
__new__
; - 核心作用:创建类对象(即最终的A),可以在创建前修改类的属性/方法(如过滤非法属性);
- 参数含义:
cls
:元类本身(如M);name
:目标类名(如"A");bases
:目标类的父类元组;attrs
:目标类的属性/方法字典;
- 返回值:必须返回调用父类
__new__
创建的类对象(如super().__new__(cls, name, bases, attrs)
)。
例如:过滤类中的非法方法,禁止类中出现test_
开头的方法,否则报错。
class M(type):def __new__(cls, name, bases, attrs):# 遍历类的所有属性/方法for attr_name, attr_value in attrs.items():# 判断是否是test_开头的函数if attr_name.startswith("test_") and callable(attr_value):raise TypeError(f"类{A}中禁止定义test_开头的方法:{attr_name}")# 创建并返回类对象return super().__new__(cls, name, bases, attrs)# 测试:定义含test_方法的类,触发报错
class A(metaclass=M):def test_func(self): # 非法方法pass# 运行结果:TypeError: 类A中禁止定义test_开头的方法:test_func

3.2. __init__
- 调用时机:
__new__
创建类对象后,元类自动调用__init__
; - 核心作用:初始化已创建的类对象(如给类添加额外属性),此时类对象已存在;
- 参数含义:
cls
:元类本身(如M);name
:目标类名(如"A");bases
:目标类的父类元组;attrs
:目标类的属性/方法字典;
- 返回值:无需返回(或返回None),仅负责初始化。
例如:创建类时,自动给类添加random_id
属性(0-100的随机数)。
import randomclass M(type):def __new__(cls, name, bases, attrs):# 先创建类对象new_class = super().__new__(cls, name, bases, attrs)return new_class # 返回创建好的类对象def __init__(cls, name, bases, attrs):# 初始化类对象:添加random_id属性cls.random_id = random.randint(0, 100)super().__init__(name, bases, attrs) # 调用父类初始化# 测试:类A由M创建,自动拥有random_id
class A(metaclass=M):passprint(A.random_id) # 输出:随机整数(如42,每次运行不同)

__new__
与__init__
的区别
对比维度 | __new__ | __init__ |
---|---|---|
调用时机 | 类创建前(先执行) | 类创建后(后执行) |
作用对象 | 待创建的类对象 | 已创建的类对象 |
核心任务 | 创建类对象 | 初始化类对象 |
返回值 | 必须返回类对象 | 无需返回(或返回None) |
3.3. __call__
- 调用时机:当调用目标类创建实例时(如
o = A()
),元类的__call__
方法被调用; - 核心作用:控制实例的创建流程(如实现单例模式:确保类只有一个实例);
- 参数含义:
cls
:目标类(如A,即元类M的实例);*args
:创建实例时传入的位置参数(如A(1, 2)
中的1, 2
);**kwargs
:创建实例时传入的关键字参数;
- 返回值:通常返回创建好的实例(如
super().__call__(*args, **kwargs)
)。
例如:确保类A只有一个实例,多次调用A()
返回同一个对象。
class M(type):# 用元类的属性存储实例(确保唯一)_instance = Nonedef __call__(cls, *args, **kwargs):# 判断实例是否已存在if cls._instance is None:# 调用父类的__call__,创建实例(底层会调用A的__new__和__init__)cls._instance = super().__call__(*args, **kwargs)# 返回已存在的实例(确保唯一)return cls._instance# 测试:类A由M创建,实现单例
class A(metaclass=M):pass# 多次创建实例,实际是同一个对象
o1 = A()
o2 = A()
print(o1 is o2) # 输出:True(o1和o2是同一个实例)
当执行o = A()
时,实际调用流程是:
M.__call__(A, *args, **kwargs)
→ 内部调用A.__new__
创建实例 → 调用A.__init__
初始化实例 → 返回实例。
4. 应用场景
元类并非日常开发必需品,仅用于解决“多个类需要统一遵循某一规则”的场景——这类场景用继承难以实现(继承只能管控子类,元类可管控所有由它创建的类)。
常见应用场景总结:
- 统一管控类的属性/方法:如禁止特定命名的方法(
test_
开头)、强制添加必需属性(如version
); - 实现设计模式:如单例模式(确保类只有一个实例)、工厂模式(统一创建实例的逻辑);
- 自动生成代码:如根据配置动态给类添加方法(无需手动定义);
- 框架级别的统一配置:如Django的ORM中,用元类(
ModelBase
)统一管理模型类的创建,自动关联数据库表。
5. super
调用
在元类的方法中使用super()
时,__new__
与__init__
/__call__
的写法不同,容易踩坑。
① __new__
中使用super()
:必须传cls
__new__
是元类的静态方法(隐式),调用super()
时需明确传入元类本身(cls
)和当前类名(name
可选,通常传cls
):
class M(type):def __new__(cls, name, bases, attrs):# 正确写法:super().__new__(cls, name, bases, attrs)return super().__new__(cls, name, bases, attrs)# 错误写法:super().__new__(name, bases, attrs)(缺少cls参数)
② __init__
/__call__
中使用super()
:无需传cls
__init__
和__call__
是元类的实例方法,调用super()
时无需额外传参(self
会自动关联):
class M(type):def __init__(cls, name, bases, attrs):# 正确写法:super().__init__(name, bases, attrs)super().__init__(name, bases, attrs)# 错误写法:super().__init__(cls, name, bases, attrs)(多传了cls参数)def __call__(cls, *args, **kwargs):# 正确写法:super().__call__(*args, **kwargs)return super().__call__(*args, **kwargs)
__new__
负责创建类对象,需要明确知道“由哪个元类创建”(因此需传cls
);__init__
/__call__
负责初始化类对象/创建实例,此时元类已确定(self
即元类实例),无需额外传参。
6. 总结
- 元类是“创建类的类”:普通类是元类的实例,默认元类是
type
; - 元类的关键方法:
__new__
:类创建前执行,创建类对象;__init__
:类创建后执行,初始化类对象;__call__
:实例化类时执行,创建实例;
- 元类的作用:解决“多个类统一管控”的问题,继承无法替代;
- 使用方式:定义元类(继承
type
)→ 目标类用metaclass
参数指定元类。
元类是Python面向对象的高级特性,掌握它能让你更深入理解Python的类模型,但无需过度追求——先保证基础功能的简洁性和可读性,再考虑用元类解决复杂场景。