python 列表的 Yaml 转储使用内联格式而不是连字符 + space

Yaml dump for python lists uses inline format instead of hypen + space

我有一个 python 有序字典,例如,

from collections import OrderedDict 
a = OrderedDict()
a['name'] = 'hello'
a['msgs'] = ['hello', 'world']

我正在将其转换为 YAML 语法,

import yaml
with open("b.yaml", 'w') as stream:
  stream.write(yaml.dump(a))

它打印,

!!python/object/apply:collections.OrderedDict
- - [name, hello]
  - - msgs
    - [hello, world]

然而,我希望使用更简单的 YAML 格式,

name : hello
msgs:
   - hello
   - world

如何强制 YAML 使用 hypen + space 符号而不是像 [a,b,c,d] 符号那样的 JSON 打印列表项?

为什么 PyYAML 将 Ordered dict 项打印为 [name, hello] 而不是 name : hello

你的问题混淆了一些东西。从您的初始示例开始,您需要显式编码才能将 a = {...} 转换为 OrderedDict。撇开这一点,这是您的预期输出:

>>> a = {
...   "name" : 'hello',
...   "msgs" : ['hello', 'world']
... }
>>> print(yaml.dump(a))
msgs: [hello, world]
name: hello

这不是您想要的。如果您阅读了专门针对此问题的 FAQ,您会发现将 default_flow_style 传递给 dump 将产生您想要的结果

>>> print(yaml.dump(a, default_flow_style=False))
msgs:
- hello
- world
name: hello

至于为什么OrderedDict会这样出来,在文档的YAML tags and Python types部分有讨论。简而言之,这样做是考虑到 Python 的 pickle 协议,并且由于 OrderedDict 在内部是 list(其中 list 是有序的;dict s 根据定义是无序的),它得到类似列表的表示。

如果您不阅读 YAML 规范,您会期望 YAML 文件中的映射是有序的,就像 YAML 文件中的文本表示是有序的一样。不幸的是,这种直观的假设是错误的,YAML 1.2 明确指出这 [应该被解释为] 一个 无序 键:值对 .

如果您使用映射和 load/change/dump,这当然会导致使用 diff 之类的工具比较 YAML 文件几乎是不可能的,并且使得将此类文件检查到修订控制系统中会导致虚假的额外修订它们在语义上相同,但在句法上不同。

我也出于其他原因开始改进 PyYAML(YAML 1.2 兼容性而不是旧的 1.1 规范、保留注释、错误修复),但是 ruamel.yaml 如果您使用它,也会将排序保留为映射round_trip_dump:

import ruamel.yaml
from ruamel.yaml.comments import CommentedMap as OrderedDict

a = OrderedDict()
a['name'] = 'hello'
a['msgs'] = ['hello', 'world']

with open("b.yaml", 'w') as stream:
    ruamel.yaml.round_trip_dump(a, stream, indent=5, block_seq_indent=3)

这会为您提供一个文件 b.yaml,内容为:

name : hello
msgs:
   - hello
   - world

完全您所期望的。

请注意,我将流传递给 round_trip_dump,如果你使用 PyYAML,你也应该这样做,因为它更有效率。
您需要使用 CommentedMap,它只是 OrderedDict/ordereddict 的薄包装,允许保留注释等
默认 indent 为 2,block_seq_indent 为 0。

如果您使用 round_trip_dump 加载文件,您将再次获得 CommentedMap,其中键的顺序将与预期一致。