大家好,我是正在实战各种 AI 项目的程序员晚枫。

元类是"类的类"。理解元类,你就能控制类的创建过程——这是 Django ORM、SQLAlchemy、pytest 等框架的底层秘密。

但《流畅的Python(第2版)》也说了一句话:"如果你不确定是否需要元类,那你就不需要元类。" 先学会什么时候用,再学怎么用。


🏗️ 类的创建过程

type 是一切类的起点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 普通类创建
class Dog:
species = "Canis lupus"

def __init__(self, name: str):
self.name = name

def bark(self) -> str:
return f"{self.name} says Woof!"

# 等价的 type() 动态创建
Dog2 = type(
'Dog', # 类名
(object,), # 基类元组
{ # 命名空间(属性和方法)
'species': 'Canis lupus',
'__init__': lambda self, name: setattr(self, 'name', name),
'bark': lambda self: f"{self.name} says Woof!",
}
)

d1 = Dog("Rex")
d2 = Dog2("Max")
print(d1.bark()) # Rex says Woof!
print(d2.bark()) # Max says Woof!
print(type(Dog)) # <class 'type'>
print(type(Dog2)) # <class 'type'>

类创建的完整流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Python 执行 class 语句时,内部步骤:
# 1. 调用 type.__prepare__() 创建命名空间
# 2. 执行类体,填充命名空间
# 3. 调用 metaclass(name, bases, namespace) 创建类对象

class Meta(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
"""步骤1:准备命名空间(Python 3.3+)"""
print(f"__prepare__ called for {name}")
namespace = super().__prepare__(name, bases, **kwargs)
return namespace

def __new__(mcs, name, bases, namespace, **kwargs):
"""步骤3:创建类对象"""
print(f"__new__ called for {name}")
cls = super().__new__(mcs, name, bases, namespace)
return cls

def __init__(cls, name, bases, namespace, **kwargs):
"""步骤4:初始化类对象"""
print(f"__init__ called for {name}")
super().__init__(name, bases, namespace)

class MyClass(metaclass=Meta):
x = 1

# 输出:
# __prepare__ called for MyClass
# __new__ called for MyClass
# __init__ called for MyClass

🔑 实用元类模式

1. 单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class SingletonMeta(type):
"""保证一个类只有一个实例的元类"""
_instances: dict = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
# 调用 type.__call__,完成实例创建
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]


class DatabaseConnection(metaclass=SingletonMeta):
def __init__(self, dsn: str):
self.dsn = dsn
print(f"连接到 {dsn}")

def query(self, sql: str) -> list:
return []


# 测试
db1 = DatabaseConnection("postgresql://localhost/mydb") # 打印:连接到...
db2 = DatabaseConnection("mysql://remote/otherdb") # 不再打印!返回同一个对象
print(db1 is db2) # True
print(db1.dsn) # postgresql://localhost/mydb

2. 自动注册插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from typing import ClassVar

class PluginMeta(type):
"""自动将子类注册到 registry 的元类"""
registry: ClassVar[dict[str, type]] = {}

def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
# 跳过基类本身
if bases:
mcs.registry[cls.__name__] = cls
return cls


class BaseProcessor(metaclass=PluginMeta):
"""处理器基类"""
def process(self, data: str) -> str:
raise NotImplementedError


class UpperProcessor(BaseProcessor):
def process(self, data: str) -> str:
return data.upper()


class ReverseProcessor(BaseProcessor):
def process(self, data: str) -> str:
return data[::-1]


class TrimProcessor(BaseProcessor):
def process(self, data: str) -> str:
return data.strip()


# 自动发现所有已注册的处理器
print(PluginMeta.registry)
# {'UpperProcessor': <class ...>, 'ReverseProcessor': <class ...>, ...}

def create_processor(name: str) -> BaseProcessor:
cls = PluginMeta.registry.get(name)
if cls is None:
raise ValueError(f"未知的处理器:{name}")
return cls()

proc = create_processor("UpperProcessor")
print(proc.process("hello")) # HELLO

3. 属性验证元类(ORM 风格)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class ModelMeta(type):
"""ORM 风格:自动收集字段定义的元类"""

def __new__(mcs, name, bases, namespace):
fields = {}
# 收集当前类中的字段定义
for key, value in namespace.items():
if isinstance(value, Field):
fields[key] = value

# 继承父类字段
for base in bases:
if hasattr(base, '_fields'):
fields.update(base._fields)

namespace['_fields'] = fields
cls = super().__new__(mcs, name, bases, namespace)
return cls


class Field:
def __init__(self, field_type: type, required: bool = True):
self.field_type = field_type
self.required = required
self.name = None

def __set_name__(self, owner, name):
self.name = name

def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)

def __set__(self, instance, value):
if value is None and self.required:
raise ValueError(f"{self.name} 不能为 None")
if value is not None and not isinstance(value, self.field_type):
raise TypeError(f"{self.name} 必须是 {self.field_type.__name__}")
instance.__dict__[self.name] = value


class Model(metaclass=ModelMeta):
def __repr__(self):
fields = {k: getattr(self, k) for k in self._fields}
return f"{type(self).__name__}({fields})"


class User(Model):
name = Field(str)
age = Field(int)
email = Field(str, required=False)

def __init__(self, name: str, age: int, email: str = None):
self.name = name
self.age = age
self.email = email


u = User("张三", 25)
print(u) # User({'name': '张三', 'age': 25, 'email': None})
print(User._fields) # {'name': ..., 'age': ..., 'email': ...}

u.age = "abc" # TypeError: age 必须是 int

🌟 现代替代方案:__init_subclass__

Python 3.6+ 提供了更简洁的方式实现很多元类功能,优先考虑用这个代替元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PluginBase:
"""用 __init_subclass__ 替代元类实现插件注册"""
_registry: dict[str, type] = {}

def __init_subclass__(cls, plugin_name: str = None, **kwargs):
super().__init_subclass__(**kwargs)
# 每当有子类定义时,自动调用
name = plugin_name or cls.__name__
PluginBase._registry[name] = cls
print(f"注册插件:{name}")


class ImagePlugin(PluginBase, plugin_name="image"):
def process(self): return "processing image"

class VideoPlugin(PluginBase, plugin_name="video"):
def process(self): return "processing video"


print(PluginBase._registry)
# {'image': <class 'ImagePlugin'>, 'video': <class 'VideoPlugin'>}

__init_subclass__ vs 元类

1
2
3
4
5
6
7
8
9
10
11
12
# 元类:控制整个类创建过程(权力最大,复杂度最高)
class ControlledMeta(type):
def __new__(mcs, name, bases, namespace):
# 可以修改命名空间、拒绝创建等
...

# __init_subclass__:只关注子类创建(简单,推荐)
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# 只能观察/修改已创建的子类
...

🔧 类装饰器:元类的轻量替代

很多情况下,类装饰器比元类更简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import functools
from typing import Any

def singleton(cls):
"""单例装饰器(比元类实现更简单)"""
instances = {}

@functools.wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]

return get_instance


@singleton
class Config:
def __init__(self, host: str = "localhost"):
self.host = host

c1 = Config("server1")
c2 = Config("server2")
print(c1 is c2) # True
print(c1.host) # server1


def auto_repr(cls):
"""自动生成 __repr__ 的类装饰器"""
def __repr__(self):
fields = {k: getattr(self, k) for k in self.__init__.__code__.co_varnames[1:]
if hasattr(self, k)}
return f"{cls.__name__}({', '.join(f'{k}={v!r}' for k, v in fields.items())})"

cls.__repr__ = __repr__
return cls


@auto_repr
class Point:
def __init__(self, x: float, y: float):
self.x = x
self.y = y

p = Point(1.0, 2.0)
print(p) # Point(x=1.0, y=2.0)

📊 三种方式对比

方式适用场景复杂度Python 版本
类装饰器修改/增强已有类2.6+
__init_subclass__监控/验证子类3.6+
元类控制类创建全过程全版本

选择原则(按优先级):

  1. 能用类装饰器解决 → 用类装饰器
  2. 需要控制子类 → 用 __init_subclass__
  3. 确实需要控制类创建的底层过程 → 才用元类

⚠️ 常见陷阱

1. 元类冲突

1
2
3
4
5
6
7
8
9
10
11
12
class Meta1(type): pass
class Meta2(type): pass

class A(metaclass=Meta1): pass
class B(metaclass=Meta2): pass

# ❌ 元类冲突!
# class C(A, B): pass # TypeError: metaclass conflict

# ✅ 解决:创建同时继承两个元类的新元类
class Meta3(Meta1, Meta2): pass
class C(A, B, metaclass=Meta3): pass

2. 误把类方法写成普通方法

1
2
3
4
5
6
7
8
9
class Meta(type):
# ❌ 错误:应该用 mcs,不是 self
def __new__(self, name, bases, namespace):
...

# ✅ 正确:元类中 self 通常写成 mcs 或 cls
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
return cls

🎯 本讲总结

type 的三重身份:普通函数(返回对象类型)、元类(所有类的默认元类)、动态创建类。

类创建流程__prepare__ → 执行类体 → __new____init__,元类可以介入每个阶段。

元类实用模式:单例、插件自动注册、ORM 字段验证、框架约束。

现代推荐:优先用 __init_subclass__(3.6+)和类装饰器,它们更简单;只有必要时才用元类。

何时用元类:需要控制类创建的底层细节时(比如修改命名空间、拦截属性赋值、实现 ORM)。


📚 推荐教材

《Python 编程从入门到实践(第 3 版)》 | 《流畅的 Python(第 2 版)》 | 《CPython 设计与实现》

学习路线: 零基础 → 《从入门到实践》 → 《流畅的 Python》 → 本门课程 → 《CPython 设计与实现》


🎓 加入《流畅的 Python》直播共读营

学到这里,如果你想系统吃透这本书——欢迎加入我的直播共读课。

  • 每周直播精讲,逐章拆解核心知识点
  • 专属学习群,随时答疑交流
  • 试运营特惠:499 元299 元

👉 【立即报名《流畅的 Python》共读课】https://mp.weixin.qq.com/s/ivHJwn1nNx5ug4TFrapvGg

🔗 课程导航

上一讲:动态属性和特性 | 下一讲:类型提示


💬 联系我

平台账号/链接
微信扫码加好友
B 站Python 自动化办公社区

主营业务:AI 编程培训、企业内训、技术咨询

🎓 AI 编程实战课程

想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!

fluent-python.png