PyYAML可以以非字母顺序转储dict项吗?

mwc*_*wcz 36 python dictionary yaml pyyaml

我正在使用yaml.dump输出字典.它根据键按字母顺序打印出每个项目.

>>> d = {"z":0,"y":0,"x":0}
>>> yaml.dump( d, default_flow_style=False )
'x: 0\ny: 0\nz: 0\n'
Run Code Online (Sandbox Code Playgroud)

有没有办法控制键/值对的顺序?

在我的特定用例中,反向打印(巧合)就足够了.但是为了完整性,我正在寻找一个答案,展示如何更精确地控制订单.

我看过使用collections.OrderedDict但是PyYAML没有(似乎)支持它.我也看过子类化yaml.Dumper,但我无法弄清楚它是否有能力改变项目顺序.

Coo*_*.Wu 47

如果您现在将PyYAML升级到5.1版本,它将支持转储而不对键进行排序,如下所示:

yaml.dump(data, default_flow_style=False, sort_keys=False)
Run Code Online (Sandbox Code Playgroud)

这是很新的东西,只是在几个小时前我输入时才修复的。

  • 谢谢@ Cooper.Wu这是一个明显的例子,在其中寻找最新答案很有帮助。 (2认同)

Ble*_*der 41

可能有更好的解决方法,但我在文档或源代码中找不到任何内容.


Python 2(见评论)

我将其子类化OrderedDict并返回一个不可解决的项目列表:

from collections import OrderedDict

class UnsortableList(list):
    def sort(self, *args, **kwargs):
        pass

class UnsortableOrderedDict(OrderedDict):
    def items(self, *args, **kwargs):
        return UnsortableList(OrderedDict.items(self, *args, **kwargs))

yaml.add_representer(UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict)
Run Code Online (Sandbox Code Playgroud)

它似乎工作:

>>> d = UnsortableOrderedDict([
...     ('z', 0),
...     ('y', 0),
...     ('x', 0)
... ])
>>> yaml.dump(d, default_flow_style=False)
'z: 0\ny: 0\nx: 0\n'
Run Code Online (Sandbox Code Playgroud)

Python 3或2(见评论)

您也可以编写自定义代表,但我不知道您以后是否会遇到问题,因为我从中删除了一些样式检查代码:

import yaml

from collections import OrderedDict

def represent_ordereddict(dumper, data):
    value = []

    for item_key, item_value in data.items():
        node_key = dumper.represent_data(item_key)
        node_value = dumper.represent_data(item_value)

        value.append((node_key, node_value))

    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)

yaml.add_representer(OrderedDict, represent_ordereddict)
Run Code Online (Sandbox Code Playgroud)

但有了这个,你可以使用本机OrderedDict类.

  • 我不确定它是否是我的Python版本(3.4),但这不起作用.我查看了源代码`yaml/representer.py:111`,你可以看到`mapping = sorted(mapping)`.它使用`sorted`内置,而不是UnsortableList的`.sort()`方法.有任何想法吗? (2认同)

wim*_*wim 12

对于 Python 3.7+,dicts 保留插入顺序。从 PyYAML 5.1.x 开始,您可以禁用键的排序 ( #254 )。不幸的是,排序键行为仍然默认为True

>>> import yaml
>>> yaml.dump({"b":1, "a": 2})
'a: 2\nb: 1\n'
>>> yaml.dump({"b":1, "a": 2}, sort_keys=False)
'b: 1\na: 2\n'
Run Code Online (Sandbox Code Playgroud)

我的项目oyaml是 PyYAML 的monkeypatch/drop-in 替代品。默认情况下,它将在所有 Python 版本和 PyYAML 版本中保留 dict 顺序。

>>> import oyaml as yaml  # pip install oyaml
>>> yaml.dump({"b":1, "a": 2})
'b: 1\na: 2\n'
Run Code Online (Sandbox Code Playgroud)

此外,它会将collections.OrderedDict子类转储为普通映射,而不是 Python 对象。

>>> from collections import OrderedDict
>>> d = OrderedDict([("b", 1), ("a", 2)])
>>> import yaml
>>> yaml.dump(d)
'!!python/object/apply:collections.OrderedDict\n- - - b\n    - 1\n  - - a\n    - 2\n'
>>> yaml.safe_dump(d)
RepresenterError: ('cannot represent an object', OrderedDict([('b', 1), ('a', 2)]))
>>> import oyaml as yaml
>>> yaml.dump(d)
'b: 1\na: 2\n'
>>> yaml.safe_dump(d)
'b: 1\na: 2\n'
Run Code Online (Sandbox Code Playgroud)


Ark*_*kun 11

单行来统治它们:

yaml.add_representer(dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))
Run Code Online (Sandbox Code Playgroud)

而已.最后.经过represent_dict这么多年和数小时之后,强大的力量已被击败dict.items()而不仅仅是让它失败了dict

下面是它的工作原理:

这是相关的PyYaml源代码:

    if hasattr(mapping, 'items'):
        mapping = list(mapping.items())
        try:
            mapping = sorted(mapping)
        except TypeError:
            pass
    for item_key, item_value in mapping:
Run Code Online (Sandbox Code Playgroud)

为了防止排序,我们只需要一些Iterable[Pair]没有的对象.items().

dict_items 是一个完美的候选人.

以下是如何在不影响yaml模块的全局状态的情况下执行此操作:

#Using a custom Dumper class to prevent changing the global state
class CustomDumper(yaml.Dumper):
    #Super neat hack to preserve the mapping key order. See https://stackoverflow.com/a/52621703/1497385
    def represent_dict_preserve_order(self, data):
        return self.represent_dict(data.items())    

CustomDumper.add_representer(dict, CustomDumper.represent_dict_preserve_order)

return yaml.dump(component_dict, Dumper=CustomDumper)
Run Code Online (Sandbox Code Playgroud)

  • 在3.7之前的Python版本上,不能为dict添加代表方法。请参阅[this Q](/sf/ask/2798622641/)及其答案。我一直在看你的答案,并对以下事实感到困惑:输出是使用`dict`而不是`OrderedDict`以键插入顺序* despite *进行排序的。幸运的是,这里使用的方法可以很容易地适应需要的人:OrderedDict:使用相同的实现,为OrderedDict而不是dict添加一个表示符,并且可以工作。 (2认同)