超越变形

YAMLObject 的一个超越变形能力,即的任意子类支持序列化反序列化

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
import yaml


class Monster(yaml.YAMLObject):
yaml_tag = u'!Monster'

def __init__(self, name, hp, ac, attacks):
self.name = name
self.hp = hp
self.ac = ac
self.attacks = attacks

def __repr__(self):
return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
self.__class__.__name__, self.name, self.hp, self.ac,
self.attacks)


yaml.load("""
--- !Monster
name: Cave spider
hp: [2,6] # 2d6
ac: 16
attacks: [BITE, HURT]
""")

Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])

print yaml.dump(Monster(
name='Cave lizard', hp=[3, 6], ac=16, attacks=['BITE', 'HURT']))
  1. 调用统一yaml.load(),可以将任意一个 YAML 序列载入为一个 Python Object
  2. 调用统一的 **yaml.dump()**,能将一个 YAMLObject 子类序列化
  3. 对于 load()dump() 的用户来说,完全不需要提前知道任何类型信息 - 超动态配置
  4. 对于 YAML 的使用者,只需要简单地继承 yaml.YAMLObject 即可,让 Python Object 具有序列化和反序列化能力
  5. YAML 动态的序列化和反序列化功能是用 Metaclass 实现的

Load

需要一个全局注册器,让 YAML 知道,序列化文本中的 !Monster 需要载入成 Monster 这个 Python 类型

1
2
3
4
5
6
7
8
registry = {}


def add_constructor(target_class):
registry[target_class.yaml_tag] = target_class


add_constructor(Monster)
  1. 建立一个全局变量 registry,把所有需要反序列化的 YAMLObject 都注册进去,如 Monster 类
  2. 缺点 - 每增加一个可反序列化的类后,都需要手动注册 – 此时可以借助 Metaclass

Metaclass

1
2
3
4
5
6
7
8
9
10
11
12
class YAMLObjectMetaclass(type):
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)

pass


class YAMLObject(metaclass=YAMLObjectMetaclass):
yaml_loader = Loader
pass
  1. YAMLObject 将 metaclass 声明为 YAMLObjectMetaclass

  2. 在 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
    8
    class 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
    6
    class 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
    12
    class 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) # 1

    metaclasstype子类,通过替换 type 的 __call__ 运算符重载机制,实现超越变形

    1. 一个类型 MyClassmetaclass 设置为 MyMeta
    2. MyClass 不再由原生的 type 创建,而是会调用 MyMeta 的 __call__ 运算符重载
    1
    2
    3
    class = type(classname, superclasses, attributedict) 
    # to
    class = MyMeta(classname, superclasses, attributedict)

    风险

    1. metaclass 会扭曲变形正常的 Python 类型模型,如使用不慎,会有极大风险
    2. metaclass 一般用于开发框架层面,在应用层面 metaclass 不是一个很好的选择