PyYAML:控制由 yaml.load() 调用的项目的顺序

新手上路,请多包涵

我有一个 yaml 设置文件,它在数据库中创建了一些记录:

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

当我使用 setting3 更新此文件并通过以下方式在数据库中重新生成记录时:

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

每次我将它们添加到数据库时,它们的设置顺序(数据库中的 ID 号)保持不变是至关重要的……而设置 3 只是附加到 yaml.load() 的末尾,这样它的 ID 就不会不要混淆数据库中已经存在的任何记录…目前每次我添加另一个设置并调用 yaml.load() 记录都会以不同的顺序加载,从而导致不同的 ID。我欢迎任何想法;)

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

按预期工作谢谢!

原文由 zzart 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 902
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 键值 tuple s,但你可以轻松地编写一个 construct_ordereddict 并获得一个 OrderedDict 如果您需要输出和输入,您还可以编写一个表示器,将 OrdereredDict 对象存储为 !omap s。

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

原文由 abarnert 发布,翻译遵循 CC BY-SA 3.0 许可协议

我的项目 oyaml 是 PyYAML 的直接替代品,它会将地图加载到 collections.OrderedDict 而不是常规的字典中。只需 pip 安装它并正常使用 - 适用于 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]')]))])

请注意,如果 stdlib 字典是保留顺序的(Python >= 3.7,CPython >= 3.6),那么 oyaml 将使用普通字典。

原文由 wim 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题