PyYAML:控制yaml.load()调用的项的排序

问题描述 投票:11回答:5

我有一个yaml设置文件,它在db中创建一些记录:

setting1:
  name: [item,item]
  name1: text
anothersetting2:
  name: [item,item]
  sub_setting:
      name :[item,item]

当我使用setting3更新此文件并在db中重新生成记录时:

import yaml
fh = open('setting.txt', 'r')
setting_list = yaml.load(fh)
for i in setting_list:
    add_to_db[i]

至关重要的是,它们的设置顺序(db中的id号)每次都保持相同,因为我将它们添加到db ...并且setting3只是附加到yaml.load()的末尾,因此它的id不会'混淆已经在数据库中的任何记录......每次我添加另一个设置并调用yaml.load()记录时,会以不同的顺序加载,这会导致不同的ID。我欢迎任何想法;)

编辑:我遵循了abarnert提示并采取了这个要点https://gist.github.com/844388

按预期工作谢谢!

python pyyaml
5个回答
2
投票

YAML规范明确指出映射中的关键顺序是不能依赖的“表示细节”。因此,如果您的设置文件依赖于映射,那么它的设置文件已经无效,如果可能的话,您最好使用有效的YAML。

当然,YAML是可扩展的,并且没有什么可以阻止您向设置文件添加“有序映射”类型。例如:

!omap setting1:
  name: [item,item]
  name1: text
!omap anothersetting2:
  name: [item,item]
  !omap sub_setting:
      name :[item,item]

你没有提到你正在使用哪个yaml模块。标准库中没有这样的模块,并且PyPI上至少有两个包提供具有该名称的模块。但是,我猜它是PyYAML,因为据我所知,这是最受欢迎的。

上面描述的扩展很容易用PyYAML解析。见http://pyyaml.org/ticket/29

def omap_constructor(loader, node):
    return loader.construct_pairs(node)
yaml.add_constructor(u'!omap', omap_constructor)

现在,而不是:

{'anothersetting2': {'name': ['item', 'item'],
  'sub_setting': 'name :[item,item]'},
 'setting1': {'name': ['item', 'item'], 'name1': 'text'}}

你会得到这个:

(('anothersetting2', (('name', ['item', 'item']),
  ('sub_setting', ('name, [item,item]'),))),
 ('setting1', (('name', ['item', 'item']), ('name1', 'text'))))

当然这会给你一个tuple的键值tuples,但是你可以轻松地编写一个construct_ordereddict并获得一个OrderedDict。如果你需要输出和输入,你也可以写一个将OrdereredDict对象存储为!omaps的表示器。

如果你真的想挂钩PyYAML让它使用OrderedDict而不是dict来进行默认映射,那么如果你已经直接在解析器对象上工作,那么这很容易做到,但是如果你想坚持使用高级别更难便利方法。幸运的是,上面链接的票证有一个可以使用的实现。请记住,您不再使用真正的YAML,而是一个变体,因此处理您的文件的任何其他软件都可能并且可能会破坏。


9
投票

我的项目oyaml是PyYAML的直接替代品,它将地图加载到collections.OrderedDict而不是常规的dicts。只需点击安装并正常使用 - 适用于Python 3和Python 2。

用你的例子演示:

>>> import oyaml as yaml  # pip install oyaml
>>> yaml.load('''setting1:
...   name: [item,item]
...   name1: text
... anothersetting2:
...   name: [item,item]
...   sub_setting:
...       name :[item,item]''')
OrderedDict([('setting1',
              OrderedDict([('name', ['item', 'item']), ('name1', 'text')])),
             ('anothersetting2',
              OrderedDict([('name', ['item', 'item']),
                           ('sub_setting', 'name :[item,item]')]))])

4
投票

您现在可以使用ruaml.yaml。

来自https://pypi.python.org/pypi/ruamel.yaml

ruamel.yaml是一个YAML解析器/发射器,支持往返保留注释,seq / map流样式和映射键顺序


1
投票

对于已知为有序字典的给定单个项,只需创建列表项并使用collections.OrderedDict:

setting1:
  - name: [item,item]
  - name1: text
anothersetting2:
  - name: [item,item]
  - sub_setting:
      name :[item,item]

import collections
import yaml
fh = open('setting.txt', 'r')
setting_list = yaml.load(fh)

setting1 = collections.OrderedDict(list(x.items())[0] for x in setting_list['setting1'])

0
投票

最后我听说,PyYAML并不支持这一点,尽管修改它可能很容易接受字典或类字典对象作为起点。

© www.soinside.com 2019 - 2024. All rights reserved.