我一直在尝试将字典转储到YAML文件。问题是,导入YAML文件的程序需要按特定顺序输入关键字。这个顺序是而不是。
import yaml
import os
baseFile = 'myfile.dat'
lyml = [{'BaseFile': baseFile}]
lyml.append({'Environment':{'WaterDepth':0.,'WaveDirection':0.,'WaveGamma':0.,'WaveAlpha':0.}})
CaseName = 'OrderedDict.yml'
CaseDir = r'C:UsersBTODocumentsProjectsMooring code testen'
CaseFile = os.path.join(CaseDir, CaseName)
with open(CaseFile, 'w') as f:
yaml.dump(lyml, f, default_flow_style=False)
生成一个*。Yml文件,格式如下:
- BaseFile: myfile.dat
- Environment:
WaterDepth: 0.0
WaveAlpha: 0.0
WaveDirection: 0.0
WaveGamma: 0.0
但是我想要的是保持顺序:
- BaseFile: myfile.dat
- Environment:
WaterDepth: 0.0
WaveDirection: 0.0
WaveGamma: 0.0
WaveAlpha: 0.0
这可能吗?
yaml.dump
有一个sort_keys
关键字参数,默认设置为True
。将其设置为False
以不重新排序:
with open(CaseFile, 'w') as f:
yaml.dump(lyml, f, default_flow_style=False, sort_keys=False)
使用OrderedDict代替dict。在开始时运行下面的设置代码。现在yaml.dump
,应该保持这个顺序。更多细节在这里和在这里
def setup_yaml():
""" https://stackoverflow.com/a/8661021 """
represent_dict_order = lambda self, data: self.represent_mapping('tag:yaml.org,2002:map', data.items())
yaml.add_representer(OrderedDict, represent_dict_order)
setup_yaml()
示例:https://pastebin.com/raw.php?i=NpcT6Yc4
PyYAML支持representer
将类实例序列化到YAML节点
yaml。YAMLObject使用元类魔法注册一个构造函数和一个表示器,前者将一个YAML节点转换为一个类实例,后者将一个类实例序列化为一个YAML节点。
在代码上方添加以下行:
def represent_dictionary_order(self, dict_data):
return self.represent_mapping('tag:yaml.org,2002:map', dict_data.items())
def setup_yaml():
yaml.add_representer(OrderedDict, represent_dictionary_order)
setup_yaml()
那么你可以使用OrderedDict
来保持yaml.dump()
的顺序:
import yaml
from collections import OrderedDict
def represent_dictionary_order(self, dict_data):
return self.represent_mapping('tag:yaml.org,2002:map', dict_data.items())
def setup_yaml():
yaml.add_representer(OrderedDict, represent_dictionary_order)
setup_yaml()
dic = OrderedDict()
dic['a'] = 1
dic['b'] = 2
dic['c'] = 3
print(yaml.dump(dic))
# {a: 1, b: 2, c: 3}
您的困难是由于在多个级别上的假设是不正确的,并且根据您的YAML解析器,可能无法透明地解析。
在Python的dict
中,键是无序的(至少对于Python <3.6)。即使密钥在源文件中有一定的顺序,一旦它们在dict
中,它们就不是:
d = {'WaterDepth':0.,'WaveDirection':0.,'WaveGamma':0.,'WaveAlpha':0.}
for key in d:
print key
给:
WaterDepth
WaveGamma
WaveAlpha
WaveDirection
如果你希望你的键是有序的,你可以使用集合。OrderedDict类型(或我自己的ruamel)。ordereddict类型,它是C语言的,而且速度快了一个数量级),您必须添加有序的键,或者作为元组列表:
from ruamel.ordereddict import ordereddict
# from collections import OrderedDict as ordereddict # < this will work as well
d = ordereddict([('WaterDepth', 0.), ('WaveDirection', 0.), ('WaveGamma', 0.), ('WaveAlpha', 0.)])
for key in d:
print key
,它将按照源文件中指定的顺序打印密钥。
第二个问题是,即使Python字典有一些键顺序恰好是你想要的,YAML规范也明确地说映射是无序的,这就是PyYAML实现Python字典转储到YAML映射的方式(反之亦然)。另外,如果转储一个ordereddict或ordereddict,通常不会得到您想要的普通YAML映射,而是得到一些带标记的YAML条目。
由于丢失顺序通常是不希望的,在您的情况下,因为您的读者假设了一些顺序,在我的情况下,因为插入/删除后键顺序不一致,这使得比较版本变得困难,我在ruamel中实现了往返一致性。所以你可以输入:
import sys
import ruamel.yaml as yaml
yaml_str = """
- BaseFile: myfile.dat
- Environment:
WaterDepth: 0.0
WaveDirection: 0.0
WaveGamma: 0.0
WaveAlpha: 0.0
"""
data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
print(data)
yaml.dump(data, sys.stdout, Dumper=yaml.RoundTripDumper)
给出您的输出结果。data
作为字典工作(' data['Environment']也是如此,但在它们下面是更智能的结构,可以保留顺序,注释,YAML锚名等)。当然,您可以更改这些(添加/删除键值对),这很容易,但您也可以从头构建这些:
import sys
import ruamel.yaml as yaml
from ruamel.yaml.comments import CommentedMap
baseFile = 'myfile.dat'
lyml = [{'BaseFile': baseFile}]
lyml.append({'Environment': CommentedMap([('WaterDepth', 0.), ('WaveDirection', 0.), ('WaveGamma', 0.), ('WaveAlpha', 0.)])})
yaml.dump(data, sys.stdout, Dumper=yaml.RoundTripDumper)
再次按照需要的顺序打印包含键的内容。我发现,与从YAML字符串开始时相比,后者的可读性较差,但它确实以更快的速度构建了lyml数据结构。
oyaml是一个python库,在转储时保留字典顺序。在字典嵌套且可能包含列表的更复杂的情况下,它特别有用。
一旦安装:
import oyaml as yaml
with open(CaseFile, 'w') as f:
f.write(yaml.dump(lyml))