python的YAML输出格式



我使用PyYaml输出YAML文件。但它重新订购了我的商品。如以下

>>> yaml.dump({'3':5, '1':3})
"{'1': 3, '3': 5}n"

我想要"{'3': 5, '1': 3}n"。我能做那件事吗

PS。我试过collections.OrderedDict。它的输出不好。如以下

>>> a= collections.OrderedDict()
>>> a['3']=1
>>> a['1']=2
>>> a['5']=2
>>> yaml.dump(a)
"!!python/object/apply:collections.OrderedDictn- - ['3', 1]n  - ['1', 2]n  - ['5', 2]n"

TL;DR:解决方案是在评论为"LOOK HERE!"的两行中。如果您接受输出将是列表列表,则可以将YAML作为程序中的dicts处理,并在存储的文件/文本中进行排序。

如果你不介意丑陋的露骨类型,比如!!python/ordered_dict或!!omap乱扔你的文件,那么你也可以走那条路。我的投票结果是!!omap,但我不确定有多少工具/lib支持它(我敢肯定支持它的工具更少!!python/ordered_dict)。最终,您要处理两组独立的数据:dict本身和定义键顺序的元数据。

(有一些半神奇的方法可以强制YAML中的有序dict,而不需要到处都是!!python/ordered_dict或!!omap混乱,但它们很脆弱,与字典的定义相矛盾,并且可能会随着底层YAML库的发展而崩溃。顺便说一句,JSON的情况也是一样的,因为YAML是JSON的超集,既不能保证键的顺序,这意味着rkaround会在符合标准的工具/用户第一次扰乱文件时中断。)

这篇文章的其余部分是示例/验证码,并解释了为什么会出现这种情况。

from __future__ import print_function
import yaml
# Setting up some example data
d = {'name': 'A Project',
'version': {'major': 1, 'minor': 4, 'patch': 2},
'add-ons': ['foo', 'bar', 'baz']}
# LOOK HERE!
ordering = ['name', 'version', 'add-ons', 'papayas']
ordered_set = [[x, d[x]] for x in ordering if x in d.keys()]
# In the event you only care about a few keys,
# you can tack the unspecified ones onto the end
# Note that 'papayas' isn't a key. You can establish an ordering that
# includes optional keys by using 'if' as a guard in the list comprehension.
# Demonstration
things = {'unordered.yaml': d, 'ordered.yaml': ordered_set}
for k in things:
f = open(k, 'w')
f.write(yaml.dump(things[k], default_flow_style=False, allow_unicode=True))
f.close()
# Let's check the result
output = []
for k in things:
f = open(k, 'r')
output.append(dict(yaml.load(f.read())))
f.close()
# Should print 'OK'
if output[0] == output[1]:
print('OK')
else:
print('Something is wrong')

创建的文件如下所示:

订购。yaml:

- - name
- A Project
- - version
- major: 1
minor: 4
patch: 2
- - add-ons
- - foo
- bar
- baz

undered.yaml:

add-ons:
- foo
- bar
- baz
name: A Project
version:
major: 1
minor: 4
patch: 2

这并没有产生您所希望的那么漂亮的YAML文档。也就是说,它可以将漂亮的YAML作为初始输入(耶!),并且编写从非漂亮的、有序的YAML到漂亮的、仍然有序的、dict风格的YAML的转换脚本是简单的(我留给您作为练习)。

如果您有一个要保留的键的顺序,请将其写入有序列表/元组中。使用该列表生成一个有序的列表列表(但不是元组列表,因为您将在YAML中获得!!python/tuple类型,这很糟糕)。把它交给YAML。要在中重新读取,请正常读取,然后将该结构传递给dict(),然后返回到您开始使用的原始字典。如果你有一个嵌套结构,需要保留它的顺序,你可能不得不递归地降低结构(这在代码中比在散文中解释更容易——这可能是你已经知道的)。

在这个例子中,我希望在文件中先有一个项目"name",然后是"version"数字元素,再是"add-ons"。通常,当您调用dump()时,PyYAML会按字母数字顺序排列字典键,但这并不可靠,因为这种情况将来可能会改变,而且YAML标准中没有任何要求,所以我不能保证不同的YAML实用程序会这样做。"addons在name之前,所以我有订购问题。所以我定义了我的顺序,然后打包一个有序的列表,然后转储它。

你要求的是内在无序的东西的秩序。字典是一个哈希表,内部只为搜索速度而排序。这种顺序是你不应该打乱的,因为如果明天发现了一种更快的实现字典的方法,运行时需要在不破坏每个人的代码的情况下实现它,因为字典是哈希表的有用抽象。

同样,YAML不是一种标记语言(毕竟,它最初代表"YAML不是标记语言"),它是一种数据格式。差异很重要。有些数据是有序的,比如元组和列表;有些不是,比如键值对的袋子(与哈希表略有不同,但在概念上相似)。

我使用这种解决方案的递归版本来保证不同YAML实现之间的YAML输出,这不是为了人类的可读性,而是因为我在YAML中传递了大量数据,每个记录都必须用密钥签名,并且无论何时使用dicts/hashes,不定顺序都会阻止统一签名。

YAML映射是无序的,Python dict也是无序的。读取文件的官方方式并且保持排序是使用CCD_ 5,但是这些在PyYAML中被转换为元组并且不像dict/ordereddict/OrderedDict那样容易更新。

如果您已经有一个要读入和更新的yaml文件,那么您可以使用我的ruamel.yaml库,该库在往返模式下用作ordereddict时读入映射,并将其作为普通映射写出(它还保留注释)。

给出了一个用法示例作为对另一个问题的回答。

我可能会迟到一点,但使用yaml包的函数add_representer似乎可以解决问题。我刚刚在yaml.dump之前添加了yaml.add_representer(collections.OrderedDict, Representer.represent_dict),我的yaml不再有上面的格式警告:

import collections
import yaml
l= collections.OrderedDict()
l['hax']=45
l['ko']=[4,5]
l['ax']="less"
yaml.dump(l)
#output:'!!python/object/apply:collections.OrderedDictn- - - haxn    - 45n  - - kon    - - 4n      - 5n  - - axn    - lessn'
#adding a representer for Ordered Dictionaries
from yaml.representer import Representer
yaml.add_representer(collections.OrderedDict, Representer.represent_dict)
yaml.dump(l)
#output'ax: lessnhax: 45nko:n- 4n- 5n'

如果有帮助,请告诉我。

另一个解决方案可能是使用oyaml,而不是本文中建议的pyyaml。

最新更新