Python 元类概览
元类控制类的创建,通常为 type 或其子类;区分元类、子类钩子与类装饰器的职责。
#concept
#growing
#dev
#python
#python
[!info] related notes
Python 元类概览
这个概念为什么出现
ORM、API 框架有时在类定义时收集字段、注册模型。这是元类或类装饰器/ __init_subclass__ 的领域。滥用元类会让代码难读。
[!abstract] 一句话理解 类是对象,其类型默认是
type;自定义元类可拦截类创建,但优先考虑__init_subclass__与类装饰器。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:插件子类自动注册(用 init_subclass)
# plugin_registry_demo.py
# 业务意图:子类定义时自动注册,无需元类。
# 教学点:多数注册需求不必元类。
class Plugin:
registry: dict[str, type] = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin.registry[cls.__name__] = cls
class ExportCsv(Plugin):
pass
class ExportJson(Plugin):
pass
def main() -> None:
print(sorted(Plugin.registry))
if __name__ == "__main__":
main()
建议运行:
python plugin_registry_demo.py
期望输出:
['ExportCsv', 'ExportJson']
结合场景再看三个关注点
- 只有执行到类定义才注册;本例不会自动发现未导入插件,并会按类名覆盖同名条目。注册键和冲突策略需另定。
__init_subclass__比元类更易组合。- 真要元类,先读框架文档。
核心概念与准确模型
- type(name, bases, namespace) 是简单动态造类的近似;class 语句还涉及元类选择、prepare 与类体执行等步骤。
- 常见自定义元类继承 type;metaclass 参数也可指定其他可调用对象,不能把子类关系当成全部形式。
- 使用类型元类时,选定的元类须为各候选元类的子类型;没有兼容候选会抛 TypeError,不会自动合并冲突。
边界情况与反直觉行为
- 多重元类冲突
- 在通常的 type 创建流程中,init_subclass 在类装饰器应用之前调用;装饰器若返回替代对象,本例注册表仍持有原类。
- 调试栈难读
常见误区
[!warning] 常见误区:学会元类就要用 错误理解:高级=更好。
正确模型:最后手段;优先子类钩子与装饰器。
工程实践
- 业务代码避免自定义元类
- 库作者写清约束
- 测试注册表隔离
本节总结
元类强大且危险。理解它为了读框架,而不是到处造轮。
自测题
- 类的默认元类?
- 注册插件优先 API?
参考答案
type。__init_subclass__或类装饰器。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Metaclasses | 规范 | 数据模型 |
| init_subclass | 规范 | 子类钩子 |