How to create a custom yaml mapping dumper for ruamel.yaml?(如何为 ruamel.yaml 创建自定义 yaml 映射转储程序?)
问题描述
我正在尝试为某些配置对象制作自定义 YAML 转储程序/加载程序.为简单起见,假设我们要将 Hero 类的对象转储到 hero.yml 文件中.
I'm trying to make a custom YAML dumper/loader for some configuration objects. For simplicity, assuming we want to dump a object of class Hero to a hero.yml file.
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
然后通过ruamel.yaml
yaml.register_class(Hero)
然后尝试转储和加载:
h = Hero('Saber', 15)
with open('config.yml', 'w') as fout:
yaml.dump(h, fout)
with open('config.yml') as fin:
yaml.load(fin)
效果很好!
但是,当我需要更灵活的行为时,需要自定义 from_yaml 和 to_yaml 方法,就会出现问题.
However, when I need a more flexible behavior, thus a custom from_yaml and to_yaml method is necessary, there is problem.
Hero的实现改为:
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def to_yaml(cls, representer, data):
return representer.represent_mapping(cls.yaml_tag,
{'name': data.name, 'age': data.age})
@classmethod
def from_yaml(cls, constructor, node):
print(node) # for debug
value = constructor.construct_mapping(node)
return cls(**value)
翻斗机按预期工作.但是加载未能加载 YAML 文件.一个抛出异常:
The dumper works just as desired. But the load failed to load the YAML file. An Exception is thrown:
243 def check_mapping_key(self, node, key_node, mapping, key, value):
244 # type: (Any, Any, Any, Any, Any) -> None
--> 245 if key in mapping:
246 if not self.allow_duplicate_keys:
247 args = [
TypeError: argument of type 'NoneType' is not iterable
通过标有for debug的print(node)行,加载的节点为:
By the print(node) line marked with for debug, the node loaded is:
MappingNode(tag='!Hero', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='name'), ScalarNode(tag='tag:yaml.org,2002:str', value='Saber')), (ScalarNode(tag='tag:yaml.org,2002:str', value='age'), ScalarNode(tag='tag:yaml.org,2002:int', value='15'))])
不使用默认 dumper/loader 的原因
此示例是显示问题的最小案例,在实际案例中,我试图仅转储对象的一部分,例如
Reason of not using default dumper/loader
This sample is a minimal case to show the problem, in real case, I'm trying to dump only part of the object, like
class A:
yaml_tag = '!A'
def __init__(self, name, age):
self.data = {'name': name, 'age': age}
A('Saber', 15)想要的YAML文件是
!A
name: Saber
age: 15
我不知道在这种情况下如何使默认的转储器/加载器工作.
I do not know how to make the default dumper/loader work in this case.
我的错误在哪里导致失败?如何解决这个问题呢?
Where is my mistake that makes this failed? How to solve this problem?
推荐答案
RoundTripConstructor.construct_mapping的定义是::
def construct_mapping(self, node, maptyp=None, deep=False)
并且它需要知道它需要什么样的映射构造.RoundTripDumper 对什么有一些期望可以附加到这样的对象,所以你最好模仿什么RoundTripDumper pass 中的例程:CommentedMap(一个普通的dict 不起作用).
and it needs to know what kind of mapping it is expected to
construct. There is some expectation in the RoundTripDumper on what
can be attached to such an object, so you best of emulating what the
routines in the RoundTripDumper pass: CommentedMap (a normal
dict is not going to work).
因此您需要执行以下操作:
So you will need to do something like:
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
yaml = YAML()
class Hero:
yaml_tag = '!Hero'
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def to_yaml(cls, representer, data):
return representer.represent_mapping(cls.yaml_tag,
{'name': data.name, 'age': data.age})
@classmethod
def from_yaml(cls, constructor, node):
data = CommentedMap()
constructor.construct_mapping(node, data, deep=True)
return cls(**data)
def __str__(self):
return "Hero(name -> {}, age -> {})".format(self.name, self.age)
yaml.register_class(Hero)
ante_hero = Hero('Saber', 15)
with open('config.yml', 'w') as fout:
yaml.dump(ante_hero, fout)
with open('config.yml') as fin:
post_hero = yaml.load(fin)
print(post_hero)
给出:
Hero(name -> Saber, age -> 15)
上面的工作是因为你的类相对简单,如果它可以有的话递归部分,您需要遵循两步创建过程,与创建的对象的初始产量,以便它可以在递归期间使用.
The above works because your class is relatively simple, if it could have recursive parts, you would need to follow a two-step creation process, with initial yield of the object created, so that it can be used during the recursion.
maptyp 默认为 None 是历史的,它必须是放.例如.construct_mapping 做的第一件事就是尝试附加评论(如果节点上有可用的评论).我将删除 0.15.55 中的默认值,这样更明智如果你把它漏掉,就会出错,就像你做的那样.
That the maptyp defaults to None is historical, it must be
set. E.g. one of the first things that construct_mapping does is
trying to attach comments (if any were available on the node).
I'll remove the default value in 0.15.55, which gives a more sensible
error if you leave it out, like you did.
这篇关于如何为 ruamel.yaml 创建自定义 yaml 映射转储程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何为 ruamel.yaml 创建自定义 yaml 映射转储程序?
基础教程推荐
- Plotly:如何设置绘图图形的样式,使其不显示缺失日期的间隙? 2022-01-01
- 在Python中从Azure BLOB存储中读取文件 2022-01-01
- 求两个直方图的卷积 2022-01-01
- 修改列表中的数据帧不起作用 2022-01-01
- 无法导入 Pytorch [WinError 126] 找不到指定的模块 2022-01-01
- 使用大型矩阵时禁止 Pycharm 输出中的自动换行符 2022-01-01
- 包装空间模型 2022-01-01
- 在同一图形上绘制Bokeh的烛台和音量条 2022-01-01
- PermissionError: pip 从 8.1.1 升级到 8.1.2 2022-01-01
- PANDA VALUE_COUNTS包含GROUP BY之前的所有值 2022-01-01
