python - 当 ruamel.yaml 从字符串加载 @dataclass 时,不会调用 __post_init__
问题描述
假设我创建了一个@dataclass class Foo
,并添加了一个__post_init__
来执行类型检查和处理。
当我尝试yaml.load
一个!Foo
对象时,__post_init__
是不会调用的。
from dataclasses import dataclass, fields
from ruamel.yaml import yaml_object, YAML
yaml = YAML()
@yaml_object(yaml)
@dataclass
class Foo:
foo: int
bar: int
def __post_init__(self):
raise Exception
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"
'''
yaml.load(s)
通过 ruamel.yaml 加载数据类时如何执行参数检查?
此行为发生在 Python 3.7 以及带有pip install dataclasses
.
解决方案
之所以__post_init__
不调用,是因为ruamel.yaml
(以及它的 s 中的 PyYAML 代码Constructor
)是在创建之前很久dataclasses
就创建的。
当然, __post_init_()
可以将调用的代码添加到的 Python 对象构造函数中,最好是在测试之后,ruamel.yaml
如果某些东西是使用在加载过程中突然调用该方法。@dataclass
__post_init_
如果你没有这样的类,你可以YAML()
在第一次加载/转储(此时构造函数被实例化)之前将你自己的更智能的构造函数添加到实例中yaml.Constructor = MyConstructor
。但是添加一个构造函数并不像继承它那么简单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
推荐阅读
- angular - 组中的自定义 ag-grid 单元格
- google-apps-script - GAS:连接到 API 时出现错误 500
- c# - 将文件上传/下载到 Google Cloud Storage 时可能出现的异常
- java - Ant+ 连接性
- java - 从 WSL 完全卸载 opendjk-11
- git - 使用 \n 作为分支名称的一部分将 mercurial 迁移到 git - “致命:预期的提交者,但没有得到”
- python - 参数化夹具问题
- apache-spark - 在 Kubernetes 上使用 Spark 启用动态分配?
- r - 如何从模块内部在闪亮的标签面板之间切换?
- python - 使用 Tensorflow Dataset map 函数检索单个图像的多个句子