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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16782112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:40:37  来源:igfitidea点击:

Can PyYAML dump dict items in non-alphabetical order?

pythondictionaryyamlpyyaml

提问by mwcz

I'm using yaml.dumpto output a dict. It prints out each item in alphabetical order based on the key.

yaml.dump用来输出一个字典。它根据键按字母顺序打印出每个项目。

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

Is there a way to control the order of the key/value pairs?

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

In my particular use case, printing in reverse would (coincidentally) be good enough. For completeness though, I'm looking for an answer that shows how to control the order more precisely.

在我的特定用例中,反向打印(巧合)就足够了。不过,为了完整起见,我正在寻找一个显示如何更精确地控制订单的答案。

I've looked at using collections.OrderedDictbut PyYAML doesn't (seem to) support it. I've also looked at subclassing yaml.Dumper, but I haven't been able to figure out if it has the ability to change item order.

我看过使用collections.OrderedDict但 PyYAML 不(似乎)支持它。我还研究了 subclassing yaml.Dumper,但我无法弄清楚它是否能够更改项目顺序。

采纳答案by Blender

There's probably a better workaround, but I couldn't find anything in the documentation or the source.

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



Python 2 (see comments)

Python 2(见评论)

I subclassed OrderedDictand made it return a list of unsortable items:

我子类化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)

And it seems to work:

它似乎有效:

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


Python 3 or 2 (see comments)

Python 3 或 2(见评论)

You can also write a custom representer, but I don't know if you'll run into problems later on, as I stripped out some style checking code from it:

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

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)

But with that, you can use the native OrderedDictclass.

但是有了它,您就可以使用本机OrderedDict类。

回答by krishnakishorek

I was also looking for an answer to the question "how to dump mappings with the order preserved?" I couldn't follow the solution given above as i am new to pyyaml and python. After spending some time on the pyyaml documentation and other forums i found this.

我也在寻找“如何在保留顺序的情况下转储映射?”这个问题的答案。我无法遵循上面给出的解决方案,因为我是 pyyaml 和 python 的新手。在 pyyaml 文档和其他论坛上花了一些时间后,我发现了这一点。

You can use the tag

你可以使用标签

!!omap

!!地图

to dump the mappings by preserving the order. If you want to play with the order i think you have to go for keys:values

通过保留顺序转储映射。如果你想按顺序玩,我认为你必须去寻找键:值

The links below can help for better understanding.

下面的链接可以帮助更好地理解。

https://bitbucket.org/xi/pyyaml/issue/13/loading-and-then-dumping-an-omap-is-broken

https://bitbucket.org/xi/pyyaml/issue/13/loading-and-then-dumping-an-omap-is-broken

http://yaml.org/type/omap.html

http://yaml.org/type/omap.html

回答by Anthon

There are two things you need to do to get this as you want:

您需要做两件事才能如愿以偿:

  • you need to use something else than a dict, because it doesn't keep the items ordered
  • you need to dump that alternative in the appropriate way.1
  • 您需要使用除 a 之外的其他东西dict,因为它不会使项目保持有序
  • 您需要以适当的方式转储该替代品。 1

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap

d = CommentedMap()
d['z'] = 0
d['y'] = 0
d['x'] = 0

ruamel.yaml.round_trip_dump(d, sys.stdout)

output:

输出:

z: 0
y: 0
x: 0


1 This was done using ruamel.yamla YAML 1.2 parser, of which I am the author.

1这是使用ruamel.yaml完成的,这是一个 YAML 1.2 解析器,我是其中的作者。

回答by orodbhen

This is really just an addendum to @Blender's answer. If you look in the PyYAMLsource, at the representer.pymodule, You find this method:

这实际上只是@Blender 答案的附录。如果你查看PyYAML源代码,在representer.py模块中,你会发现这个方法:

def represent_mapping(self, tag, mapping, flow_style=None):
    value = []
    node = MappingNode(tag, value, flow_style=flow_style)
    if self.alias_key is not None:
        self.represented_objects[self.alias_key] = node
    best_style = True
    if hasattr(mapping, 'items'):
        mapping = mapping.items()
        mapping.sort()
    for item_key, item_value in mapping:
        node_key = self.represent_data(item_key)
        node_value = self.represent_data(item_value)
        if not (isinstance(node_key, ScalarNode) and not node_key.style):
            best_style = False
        if not (isinstance(node_value, ScalarNode) and not node_value.style):
            best_style = False
        value.append((node_key, node_value))
    if flow_style is None:
        if self.default_flow_style is not None:
            node.flow_style = self.default_flow_style
        else:
            node.flow_style = best_style
    return node

If you simply remove the mapping.sort()line, then it maintains the order of items in the OrderedDict.

如果您只是删除该mapping.sort()行,那么它会保持OrderedDict.

Another solution is given in this post. It's similar to @Blender's, but works for safe_dump. The common element is the converting of the dict to a list of tuples, so the if hasattr(mapping, 'items')check evaluates to false.

这篇文章中给出了另一种解决方案。它类似于@Blender 的,但适用于safe_dump. 常见的元素是将 dict 转换为元组列表,因此if hasattr(mapping, 'items')检查结果为 false。

Update:

更新:

I just noticed that The Fedora Project's EPEL repo has a package called python2-yamlordereddictloader, and there's one for Python 3 as well. The upstream project for that package is likely cross-platform.

我刚刚注意到 Fedora 项目的 EPEL 存储库有一个名为 的包python2-yamlordereddictloader,并且还有一个用于 Python 3的包。该包的上游项目可能是跨平台的。

回答by wackazong

Building on @orodbhen's answer:

基于@orodbhen 的回答:

old_sorted = __builtins__['sorted']
__builtins__['sorted'] = lambda x: x
with open(filename, 'w') as outfile:
    yaml.dump(f_json, outfile)
__builtins['sorted'] = old_sorted

Just replace the built-in function sorted by a lambda identity function while you use yaml.dump.

只需在使用 yaml.dump 时替换按 lambda 标识函数排序的内置函数即可。

回答by wim

For Python 3.7+, dicts preserve insertion order. It's best to use a library which respects that, such as my project oyamlwhich is a monkeypatch/drop-in replacement for PyYAML:

对于 Python 3.7+,dicts 保留插入顺序。最好使用尊重这一点的库,例如我的项目oyaml,它是 PyYAML 的monkeypatch/drop-in 替代品:

>>> import oyaml as yaml  # pip install oyaml
>>> d = {"z": 0, "y": 0, "x": 0}
>>> yaml.dump(d, default_flow_style=False)
'z: 0\ny: 0\nx: 0\n'

回答by Ark-kun

One-liner to rule them all:

单线统治他们:

yaml.add_representer(dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))

That's it. Finally. After all those years and hours, the mighty represent_dicthas been defeated by giving it the dict.items()instead of just dict

就是这样。最后。毕竟这些年,小时,浩浩荡荡represent_dict已经击败了给它dict.items(),而不是仅仅dict

Here is how it works:

下面是它的工作原理:

This is the relevant PyYaml source code:

这是相关的 PyYaml 源代码:

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

To prevent the sorting we just need some Iterable[Pair]object that does not have .items().

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

dict_itemsis a perfect candidate for this.

dict_items是一个完美的候选人。

Here is how to do this without affecting the global state of the yaml module:

以下是在不影响 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)

回答by Cooper.Wu

If you upgrade PyYAML to 5.1 version, now, it supports dump without sorting the keys like this:

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

yaml.dump(data, default_flow_style=False, sort_keys=False)

this is very new, just being fixed few hours ago when I typing.

这是非常新的,几个小时前我打字时才修复。

回答by Peter Ba?ista

If safe_dump(i.e. dumpwith Dumper=SafeDumper) is used, then calling yaml.add_representerhas no effect. In such case it is necessary to call add_representermethod explicitly on SafeRepresenterclass:

如果使用safe_dump(即dumpwith Dumper=SafeDumper),则调用yaml.add_representer无效。在这种情况下,有必要add_representerSafeRepresenter类上显式调用方法:

yaml.representer.SafeRepresenter.add_representer(
    OrderedDict, ordered_dict_representer
)