TL; DR:解决方案在两行注释“看这里!” 如果您接受输出将是列表列表,则可以在程序中将 YAML 作为 dicts 处理,并在存储的文件/文本中进行排序。
如果您不介意像 !!python/ordered_dict 或 !!omap 这样的丑陋的显式类型乱扔文件,那么您也可以走这条路。我的投票投给了!!omap,但我不确定有多少工具/库支持它(不过,我很确定支持 !!python/ordered_dict 的工具更少)。最终,您要处理两组独立的数据:dict 本身和定义键顺序的元数据。
(在没有 !!python/ordered_dict 或 !!omap 的情况下,有一些半神奇的方法可以在 YAML 中强制一个有序的字典,但它们很脆弱,与字典的定义相矛盾,并且可能会随着底层 YAML 库的发展而中断. 顺便说一下,这种情况对于 JSON 是相同的,因为 YAML 是 JSON 的超集,并且不保证键的顺序——这意味着当符合标准的工具/用户第一次弄乱文件时,变通方法会中断。)
这篇文章的其余部分是示例/验证代码,并解释了为什么会这样。
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
无序的.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() ,然后您将返回到您开始使用的原始字典。如果您有一个需要保留其顺序的嵌套结构,您可能必须递归地降低该结构(这在代码中比在散文中解释更容易 - 这是您可能已经知道的)。
在此示例中,我希望文件中首先出现项目“名称”,然后是“版本”编号元素,然后是“附加组件”。通常,当您调用 dump() 时,PyYAML 按字母数字顺序对字典键进行排序,但这并不可靠,因为将来可能会发生变化,并且 YAML 标准中没有任何要求,所以我不能保证不同的 YAML 实用程序会以这种方式做事。“附加组件”在“名称”之前,所以我有一个订购问题。所以我定义了我的顺序,然后打包一个有序列表,然后转储它。
您要求从本质上无序的事物中获得秩序。字典是一个哈希表,内部排序专门用于搜索速度。该顺序是您不应该弄乱的,因为如果明天发现了一种更快的实现字典的方法,则运行时需要在不破坏每个人的代码的情况下实现它,而字典依赖于作为哈希表的有用抽象的每个人的代码。
同理,YAML 不是一种标记语言(毕竟它最初代表“Yaml Ain't a Markup Language”),它是一种数据格式。区别很重要。一些数据是有序的,比如元组和列表;有些不是,比如成袋的键值对(与哈希表略有不同,但在概念上相似)。
我使用这种解决方案的递归版本来保证跨不同 YAML 实现的 YAML 输出,不是为了人类可读性,而是因为我在 YAML 中传递大量数据并且每个记录都必须使用密钥进行签名,并且不确定的顺序可以防止使用字典/哈希时统一签名。