大家好,我是正在实战各种 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!"
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()) print(d2.bark()) print(type(Dog)) print(type(Dog2))
|
类创建的完整流程
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
|
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
|
🔑 实用元类模式
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: 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) print(db1.dsn)
|
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)
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"))
|
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) print(User._fields)
u.age = "abc"
|
🌟 现代替代方案:__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)
|
__init_subclass__ vs 元类
1 2 3 4 5 6 7 8 9 10 11 12
| class ControlledMeta(type): def __new__(mcs, name, bases, namespace): ...
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) print(c1.host)
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)
|
📊 三种方式对比
| 方式 | 适用场景 | 复杂度 | Python 版本 |
|---|
| 类装饰器 | 修改/增强已有类 | 低 | 2.6+ |
__init_subclass__ | 监控/验证子类 | 低 | 3.6+ |
| 元类 | 控制类创建全过程 | 高 | 全版本 |
选择原则(按优先级):
- 能用类装饰器解决 → 用类装饰器
- 需要控制子类 → 用
__init_subclass__ - 确实需要控制类创建的底层过程 → 才用元类
⚠️ 常见陷阱
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 Meta3(Meta1, Meta2): pass class C(A, B, metaclass=Meta3): pass
|
2. 误把类方法写成普通方法
1 2 3 4 5 6 7 8 9
| class Meta(type): def __new__(self, name, bases, namespace): ... 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
🔗 课程导航
← 上一讲:动态属性和特性 | 下一讲:类型提示 →
💬 联系我
主营业务:AI 编程培训、企业内训、技术咨询
🎓 AI 编程实战课程
想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!
