【问题标题】:PyYAML : Control ordering of items called by yaml.load()PyYAML:控制由 yaml.load() 调用的项目的顺序
【发布时间】:2012-11-08 21:02:22
【问题描述】:

我有一个 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 时,它们的设置顺序(db 中的 id 编号)都保持相同......而 setting3 只是被附加到 yaml.load() 的末尾,这样它的id 不会混淆数据库中已经存在的任何记录... 目前,每次我添加另一个设置并调用 yaml.load() 记录都会以不同的顺序加载,从而导致不同的 id。我欢迎任何想法;)

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

按预期工作,谢谢!

【问题讨论】:

    标签: python pyyaml


    【解决方案1】:

    我的项目 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 将使用普通字典。

    【讨论】:

    • 该项目是否仍处于活动状态并与 Python 3.8+ 兼容?
    • @quassy 是的,是的
    • 非常感谢!很有用!
    【解决方案2】:

    您现在可以为此使用 ruaml.yaml

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

    ruamel.yaml 是一个支持往返的 YAML 解析器/发射器 保存 cmets、seq/map 流样式和 map 键顺序

    【讨论】:

    • 等等,但我试过了,它返回一个普通的字典。倾倒它以错误的顺序给出了密钥。您能否详细说明如何执行此操作?
    • 您应该将Loader = ruamel.yaml.RoundTripLoader 传递给load 方法以保留订单。
    【解决方案3】:

    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'))))
    

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

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

    【讨论】:

      【解决方案4】:

      对于已知为有序字典的给定单个项目,只需制作列表中的项目并使用集合。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'])
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 这真的行不通。首先,YAML 文档不必是字典。其次,如果您以OrderedDict 开头,则所有子字典仍将是dict。所以你真正需要的是修改它以接受一个不同的构造函数来代替dict(理想情况下,它使用的每个构造函数都有一个,尽管其他构造函数不会经常有用)。
        猜你喜欢
        • 2013-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-25
        • 2023-01-04
        • 2021-05-14
        相关资源
        最近更新 更多