之所以没有调用__post_init__,是因为ruamel.yaml(以及其Constructors 中的PyYAML 代码)早在dataclasses 创建之前就已创建。
当然,可以将调用__post_init_() 的代码添加到ruamel.yaml 的Python 对象构造函数中,最好在测试是否使用@dataclass 创建某些东西之后,否则为非Data-Class 类,即恰好有这样一个名为__post_init_ 的方法,在加载过程中会突然调用该方法。
如果你没有这样的类,你可以在第一次加载/转储之前使用yaml.Constructor = MyConstructor向YAML()实例添加你自己的更智能的构造函数(此时构造函数被实例化)。但是添加一个构造函数并不像继承RoundTripConstructor那么简单,因为所有支持的节点类型都需要在这样一个新的构造函数类型上注册。
大多数时候,我发现在RoundTripConstructor 上修补适当的方法会更容易:
from dataclasses import dataclass, fields
from ruamel.yaml import yaml_object, YAML, RoundTripConstructor
def my_construct_yaml_object(self, node, cls):
for data in self.org_construct_yaml_object(node, cls):
yield data
# not doing a try-except, in case `__post_init__` does catch the AttributeError
post_init = getattr(data, '__post_init__', None)
if post_init:
post_init()
RoundTripConstructor.org_construct_yaml_object = RoundTripConstructor.construct_yaml_object
RoundTripConstructor.construct_yaml_object = my_construct_yaml_object
yaml = YAML()
yaml.preserve_quotes = True
@yaml_object(yaml)
@dataclass
class Foo:
foo: int
bar: int
def __post_init__(self):
for field in fields(self):
value = getattr(self, field.name)
typ = field.type
if not isinstance(value, typ):
raise Exception
s = '''\
!Foo
foo: "foo"
bar: "bar"
'''
d = yaml.load(s)
抛出异常:
Traceback (most recent call last):
File "try.py", line 36, in <module>
d = yaml.load(s)
File "/home/venv/tmp-46489abf428c4cd4/lib/python3.7/site-packages/ruamel/yaml/main.py", line 266, in load
return constructor.get_single_data()
File "/home/venv/tmp-46489abf428c4cd4/lib/python3.7/site-packages/ruamel/yaml/constructor.py", line 105, in get_single_data
return self.construct_document(node)
File "/home/venv/tmp-46489abf428c4cd4/lib/python3.7/site-packages/ruamel/yaml/constructor.py", line 115, in construct_document
for dummy in generator:
File "try.py", line 10, in my_construct_yaml_object
post_init()
File "try.py", line 29, in __post_init__
raise Exception
Exception
请注意,YAML 中的双引号是多余的,因此如果您想在往返时保留这些双引号,则需要 yaml.preserve_quotes = True