Can PyYAML dump dict items in non-alphabetical order?

后端 未结 10 641
一向
一向 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:31

    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.¹

    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
    

    ¹ This was done using ruamel.yaml a YAML 1.2 parser, of which I am the author.

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

    Building on @orodbhen's answer:

    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.

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

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

    yaml.dump(data, sort_keys=False)
    

    As shown in help(yaml.Dumper), sort_keys defaults to True:

    Dumper(stream, default_style=None, default_flow_style=False,
      canonical=None, indent=None, width=None, allow_unicode=None,
      line_break=None, encoding=None, explicit_start=None, explicit_end=None,
      version=None, tags=None, sort_keys=True)
    

    (These are passed as kwargs to yaml.dump)

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

    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_dict has been defeated by giving it the dict.items() instead of just dict

    Here is how it works:

    This is the relevant PyYaml source code:

        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().

    dict_items is a perfect candidate for this.

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

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