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

Python PyYAML:控制yaml.load()调用的项的顺序,python,pyyaml,Python,Pyyaml,我有一个yaml设置文件,它在db中创建了一些记录: 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.lo

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

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]
重要的是,每次将它们添加到数据库时,它们的设置顺序(db中的id号)保持不变。。。setting3只是附加到yaml.load()的末尾,这样它的id就不会混淆数据库中已有的任何记录。。。 此时,每次我添加另一个设置并调用yaml.load()时,记录以不同的顺序加载,从而产生不同的ID。我欢迎任何意见;)

编辑: 我遵循了abarnert的建议,并采纳了这个要点


工作如期,谢谢

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

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

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

!omap setting1:
  name: [item,item]
  name1: text
!omap anothersetting2:
  name: [item,item]
  !omap sub_setting:
      name :[item,item]
>>> 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]')]))])
您没有提到您正在使用哪个
yaml
模块。标准库中没有这样的模块,PyPI上至少有两个包提供了具有该名称的模块。然而,我猜是Pyaml,因为据我所知,这是最流行的

上面描述的扩展很容易用PyYAML解析。见:

现在,不是:

{'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
s的
tuple
,但是您可以轻松地编写一个构造,并获得一个
orderedict
。您还可以编写一个representer,将
ordereddict
对象存储为
!omap
s,如果您需要输出和输入


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

您现在可以使用ruaml.YAML来实现此目的

发件人:

yaml是一个支持往返的yaml解析器/发射器 保留注释、顺序/映射流样式和映射键顺序


对于已知为有序字典的给定单个项,只需将其作为列表项和已用集合。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'])
我的项目是PyYAML的替代品,PyYAML将把地图加载到
集合中。OrderedDict
而不是常规dict。只需pip安装它并正常使用即可-在Python 3和Python 2上都可以使用

用您的示例演示:

!omap setting1:
  name: [item,item]
  name1: text
!omap anothersetting2:
  name: [item,item]
  !omap sub_setting:
      name :[item,item]
>>> 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 dict是保序的(Python>=3.7,CPython>=3.6),那么
oyaml将使用普通dict。

这实际上不起作用。首先,YAML文档不必是字典。其次,如果您从一个
OrderedDict
开始,那么所有子字典仍然是
dict
。因此,您真正需要的是修改它,以接受不同的构造函数来代替
dict
(理想情况下,它使用的每个构造函数都有一个构造函数,尽管其他构造函数不会经常有用)。等等,但我尝试了它,它返回了一个常规dict。转储它会以错误的顺序提供密钥。你能详细说明一下怎么做吗?你应该将
Loader=ruamel.yaml.RoundTripLoader
传递给
load
方法以保持顺序。该项目是否仍然处于活动状态并与Python 3.8+兼容?@quassy Yes和yesThanks很多!它非常有用!