在保留顺序的同时将字典转储到YAML文件

时间:2015-07-24 07:58:09

标签: python-3.x dictionary yaml

我一直在尝试将字典转储到YAML文件中。问题是导入YAML文件的程序需要按特定顺序的关键字。此订单按字母顺序

SELECT REPLACE(column_name, ' ', '') FROM table_name

这会生成一个* .yml文件,其格式如下:

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:\Users\BTO\Documents\Projects\Mooring code testen'
CaseFile = os.path.join(CaseDir, CaseName)
with open(CaseFile, 'w') as f:
    yaml.dump(lyml, f, default_flow_style=False)

但我想要的是保留订单:

- BaseFile: myfile.dat
- Environment:
    WaterDepth: 0.0
    WaveAlpha: 0.0
    WaveDirection: 0.0
    WaveGamma: 0.0

这可能吗?

4 个答案:

答案 0 :(得分:26)

使用OrderedDict代替dict。在开始时运行以下设置代码。现在yaml.dump,应保留订单。更多详情herehere

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

答案 1 :(得分:4)

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}

答案 2 :(得分:1)

您的困难是由于多个级别的假设不正确而导致的,并且取决于您的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

如果你想要你的密钥订购你可以使用collections.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 dict有一些恰好是你想要的密钥排序,YAML规范明确地说明映射是无序的,这就是例如PyYAML实现了将Python dict转储到YAML映射(反之亦然)。 此外,如果您转储ordereddict或OrderedDict,通常不会获得您指定所需的普通YAML映射,但会获得一些标记为YAML的条目。

由于丢失订单通常是不可取的,因为您的读者会假定某些订单,在我的情况下因为这使得比较版本很困难,因为插入/删除后密钥排序不一致,我实现了往返一致性在ruamel.yaml中,您可以这样做:

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作为dict工作(`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数据结构的速度更快。

答案 3 :(得分:0)

3年后-yaml.dump的sort_keys kwarg默认设置为True。将其设置为False即可不重新排序:

with open(CaseFile, 'w') as f:
    yaml.dump(lyml, f, default_flow_style=False, sort_keys=False)