格式化 PyYAML 转储()输出
Posted
技术标签:
【中文标题】格式化 PyYAML 转储()输出【英文标题】:Formatting PyYAML dump() output 【发布时间】:2012-12-23 03:04:34 【问题描述】:我有一个字典列表,我想对其进行序列化:
list_of_dicts = [ 'key_1': 'value_a', 'key_2': 'value_b',
'key_1': 'value_c', 'key_2': 'value_d',
...
'key_1': 'value_x', 'key_2': 'value_y' ]
yaml.dump(list_of_dicts, file, default_flow_style = False)
产生以下内容:
- key_1: value_a
key_2: value_b
- key_1: value_c
key_2: value_d
(...)
- key_1: value_x
key_2: value_y
但我想得到这个:
- key_1: value_a
key_2: value_b
<-|
- key_1: value_c |
key_2: value_d | empty lines between blocks
(...) |
<-|
- key_1: value_x
key_2: value_y
PyYAML documentation 非常简短地讨论了 dump()
参数,似乎没有关于这个特定主题的任何内容。
手动编辑文件以添加换行符大大提高了可读性,之后结构仍然可以正常加载,但我不知道如何使转储方法生成它。
一般来说,除了简单的缩进之外,还有什么方法可以更好地控制输出格式?
【问题讨论】:
【参考方案1】:使用库没有简单的方法来做到这一点(yaml dumper 语法树中的节点对象是被动的,不能发出这个信息),所以我最终得到了
stream = yaml.dump(list_of_dicts, default_flow_style = False)
file.write(stream.replace('\n- ', '\n\n- '))
【讨论】:
谢谢!必须使用类似的东西来格式化列表。 PyYAML 不会在-
之前放置缩进,而我们使用的 YAML 消费库预计会有一些缩进。所以我们不得不做replace('- ', ' - ')
该节点是被动的是真实的但无关紧要,因为节点也不会发出任何其他信息(即ScalarNode
s 不会发出自己的值)。 Emitter
确实获得了一个节点的值(在适当的时候)并发出它,如果你在节点上附加额外的信息,并增强相关的 Emitter 方法来处理这些额外的信息(就像我在 ruamel.yaml
中所做的那样),那么就会有绝对不需要做那种基于字符串的粗略的后处理。
@Andrei 使用ruamel.yaml
,您可以设置yaml.indent(sequence=3, offset=1)
并在不进行后处理的情况下获得该输出。【参考方案2】:
虽然有点笨拙,但我的目标与 OP 相同。 我通过子类化 yaml.Dumper 解决了它
from yaml import Dumper
class MyDumper(Dumper):
def write_indent(self):
indent = self.indent or 0
if not self.indention or self.column > indent \
or (self.column == indent and not self.whitespace):
self.write_line_break()
##########$#######################################
# On the first level of indentation, add an extra
# newline
if indent == 2:
self.write_line_break()
##################################################
if self.column < indent:
self.whitespace = True
data = u' '*(indent-self.column)
self.column = indent
if self.encoding:
data = data.encode(self.encoding)
self.stream.write(data)
你这样称呼它:
print dump(python_dict, default_flow_style=False, width=79, Dumper=MyDumper)
【讨论】:
受此启发,我创建了另一个版本github.com/yaml/pyyaml/issues/127#issuecomment-525800484【参考方案3】:PyYAML 文档只简要讨论了dump()
参数,因为没有太多要说的。 PyYAML 不提供这种控件。
为了允许在加载的 YAML 中保留这些空(和注释)行,我开始开发 ruamel.yaml
库,它是停滞的 PyYAML 的超集,具有 YAML 1.2 兼容性,添加了许多功能并修复了错误。使用ruamel.yaml
,您可以:
import sys
import ruamel.yaml
yaml_str = """\
- key_1: value_a
key_2: value_b
- key_1: value_c
key_2: value_d
- key_1: value_x # a few before this were ellipsed
key_2: value_y
"""
yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
并得到与输入字符串完全相同的输出(包括注释)。
您还可以从头开始构建您想要的输出:
import sys
import ruamel.yaml
yaml = ruamel.yaml.YAML()
list_of_dicts = yaml.seq([ 'key_1': 'value_a', 'key_2': 'value_b',
'key_1': 'value_c', 'key_2': 'value_d',
'key_1': 'value_x', 'key_2': 'value_y' ])
for idx in range(1, len(list_of_dicts)):
list_of_dicts.yaml_set_comment_before_after_key(idx, before='\n')
ruamel.yaml.comments.dump_comments(list_of_dicts)
yaml.dump(list_of_dicts, sys.stdout)
使用yaml.seq()
的转换对于创建一个允许通过特殊属性附加空行的对象是必要的。
该库还允许在字符串、int 格式(十六进制、八进制、二进制)和浮点数上保存/轻松设置引号和文字样式。以及用于映射和序列的单独缩进规范(尽管不是针对单个映射或序列)。
【讨论】:
很好的答案,为你自己改进的 yaml 解析器分叉而欢呼,我现在就去尝试一下,因为我现在自己正在研究转储格式问题。不过我突然想到,pyyaml 自从这篇文章以来一直在更新。从那以后,他们在格式方面有所改进吗?或者您的任何贡献是否已合并到 pyyaml 中? 两个问题都没有。我所知道的唯一真正的机会是,如果没有 100% 的输入控制,PyYAML 的load()
将不再那么危险。它仍然是 YAML 1.1,多个长期存在的错误仍未修复。更改似乎主要是为了与较新版本的 Python 兼容。我认为我的(和所有其他 PR)随着从 bitbucket 到 github 的迁移而被丢弃(如果我在修正 PyYAML 恕我直言,这一努力会更好)。 ruamel.yaml 现在可以在项目指示符 (-
) 之后控制额外的换行符:.compact_seq_seq
resp。 .compact_seq_map
以上是关于格式化 PyYAML 转储()输出的主要内容,如果未能解决你的问题,请参考以下文章