将字典转储到YAML文件,同时保持顺序



我一直在尝试将字典转储到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))

最新更新