Python - Metaclass
超越变形
YAMLObject 的一个超越变形能力,即的任意子类支持序列化和反序列化
1 | import yaml |
- 调用统一的 yaml.load(),可以将任意一个 YAML 序列载入为一个 Python Object
- 调用统一的 **yaml.dump()**,能将一个 YAMLObject 子类序列化
- 对于 load() 和 dump() 的用户来说,完全不需要提前知道任何类型信息 - 超动态配置
- 对于 YAML 的使用者,只需要简单地继承 yaml.YAMLObject 即可,让 Python Object 具有序列化和反序列化能力
- YAML 动态的序列化和反序列化功能是用 Metaclass 实现的
Load
需要一个全局注册器,让 YAML 知道,序列化文本中的
!Monster
需要载入成 Monster 这个 Python 类型
1 | registry = {} |
- 建立一个全局变量 registry,把所有需要反序列化的 YAMLObject 都注册进去,如 Monster 类
- 缺点 - 每增加一个可反序列化的类后,都需要手动注册 – 此时可以借助 Metaclass
Metaclass
1 | class YAMLObjectMetaclass(type): |
YAMLObject 将 metaclass 声明为 YAMLObjectMetaclass
在 YAMLObjectMetaclass 中的核心 -
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
- YAML 应用 metaclass,拦截了所有 YAMLObject 子类的定义
- 用户在定义任何 YAMLObject 子类时,Python 会强行插入代码
- 所以 YAML 使用者,无需去手动注册了,只管定义即可
Metaclass
Python 语言的底层实现 - Metaclass 能够拦截 Python 类的定义
所有 Python 的用户定义类,都是 type 这个类的实例
类本身是 type 类的实例 - 与 Java 中的 Class 类似1
2
3
4
5
6
7
8class MyClass:
pass
instance = MyClass()
print(type(instance)) # <class '__main__.MyClass'>
print(type(MyClass)) # <class 'type'>用户自定义类,不过是 type 类的
__call__
运算符重载
在定义一个类的语句结束时,真正发生的是 Python 调用 type 的__call__
运算符1
2
3
4
5
6class MyClass:
data = 1
# 真正执行 - type 的 __call__ 运算符重载
# class = type(classname, superclasses, attributedict)
# class = type('MyClass', (object,), {'data':1})进一步调用:new + init
1
2# type.__new__(typeclass, classname, superclasses, attributedict)
# type.__init__(class, classname, superclasses, attributedict)代码验证
1
2
3
4
5
6
7
8
9
10
11
12class MyClass:
data = 1
instance = MyClass()
print(MyClass, instance) # <class '__main__.MyClass'> <__main__.MyClass object at 0x104e0e488>
print(instance.data) # 1
MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
print(MyClass, instance) # <class '__main__.MyClass'> <__main__.MyClass object at 0x104e0e6c8>
print(instance.data) # 1metaclass 是 type 的子类,通过替换 type 的
__call__
运算符重载机制,实现超越变形- 一个类型 MyClass 的 metaclass 设置为 MyMeta
- MyClass 不再由原生的 type 创建,而是会调用 MyMeta 的
__call__
运算符重载
1
2
3class = type(classname, superclasses, attributedict)
# to
class = MyMeta(classname, superclasses, attributedict)风险
- metaclass 会扭曲变形正常的 Python 类型模型,如使用不慎,会有极大风险
- metaclass 一般用于开发框架层面,在应用层面 metaclass 不是一个很好的选择
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.