Can PyYAML dump dict items in non-alphabetical order?

后端 未结 10 640
一向
一向 2020-11-29 03:38

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

>>> d = {\"z\":0,\"y\":0,\"x\":         


        
相关标签:
10条回答
  • 2020-11-29 04:14

    If safe_dump (i.e. dump with Dumper=SafeDumper) is used, then calling yaml.add_representer has no effect. In such case it is necessary to call add_representer method explicitly on SafeRepresenter class:

    yaml.representer.SafeRepresenter.add_representer(
        OrderedDict, ordered_dict_representer
    )
    
    0 讨论(0)
  • 2020-11-29 04:14

    The following setting makes sure the content is not sorted in the output:

    yaml.sort_base_mapping_type_on_output = False
    
    0 讨论(0)
  • 2020-11-29 04:19

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


    Python 2 (see comments)

    I subclassed OrderedDict and made it return a list of unsortable items:

    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)

    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 OrderedDict class.

    0 讨论(0)
  • 2020-11-29 04:19

    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.

    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

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

    0 讨论(0)
  • 2020-11-29 04:28

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

    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.

    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.

    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.

    0 讨论(0)
  • 2020-11-29 04:30

    For Python 3.7+, dicts preserve insertion order. Since PyYAML 5.1.x, you can disable the sorting of keys (#254). Unfortunately, the sorting keys behaviour does still default to 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'
    

    My project oyaml is a monkeypatch/drop-in replacement for PyYAML. It will preserve dict order by default in all Python versions and PyYAML versions.

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

    Additionally, it will dump the collections.OrderedDict subclass as normal mappings, rather than Python objects.

    >>> 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'
    
    0 讨论(0)
提交回复
热议问题