Может ли PyYAML сбрасывать элементы dict в не алфавитном порядке?
Я использую yaml.dump
для вывода dict. Он выводит каждый элемент в алфавитном порядке на основе ключа.
>>> d = {"z":0,"y":0,"x":0}
>>> yaml.dump( d, default_flow_style=False )
'x: 0ny: 0nz: 0n'
есть ли способ контролировать порядок пар ключ/значение?
в моем конкретном случае использования печать в обратном порядке (по совпадению) была бы достаточно хороша. Для полноты, однако, я ищу ответ, который показывает, как более точно контролировать порядок.
Я посмотрел на использование collections.OrderedDict
но PyYAML (кажется) не поддерживает его. Я также посмотрел на подклассы yaml.Dumper
, но я не смог выяснить, имеет ли он возможность изменять порядок элементов.
7 ответов
вероятно, есть лучший обходной путь, но я не мог найти ничего в документации или источнике.
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)
и, кажется, работает:
>>> d = UnsortableOrderedDict([
... ('z', 0),
... ('y', 0),
... ('x', 0)
... ])
>>> yaml.dump(d, default_flow_style=False)
'z: 0\ny: 0\nx: 0\n'
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)
но с этим, вы можете использовать родной OrderedDict
класса.
есть две вещи, которые нужно сделать, чтобы получить это, как вы хотите:
- вы должны использовать что-то другое, чем
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)
выход:
z: 0
y: 0
x: 0
1 это было сделано с помощью ruamel.и YAML парсер YAML 1.2, автором которого я являюсь.
это действительно просто дополнение к ответу @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
если вы просто удалите mapping.sort()
строка, затем она поддерживает порядок элементов в OrderedDict
.
другое решение дано в этот пост. Это похоже на @Blender, но работает для safe_dump
. Общим элементом является преобразование dict в список кортежей, поэтому if hasattr(mapping, 'items')
проверить оценивает значение false.
обновление:
Я только что заметил, что EPEL repo проекта Fedora имеет пакет под названием python2-yamlordereddictloader
, и есть один для Python 3, а также. Восходящий проект для этого пакета, вероятно, кросс-платформенный.
Я также искал ответ на вопрос "как сбросить сопоставления с сохраненным порядком?"Я не мог следовать приведенному выше решению, поскольку я новичок в pyyaml и python. Потратив некоторое время на документацию pyyaml и другие форумы, я нашел это.
вы можете использовать тег
!!omap
чтобы сбросить сопоставления, сохранив порядок. Если вы хотите играть с порядком, я думаю, вам нужно пойти на ключи: values
ссылки ниже может помочь для лучшего понимания.
https://bitbucket.org/xi/pyyaml/issue/13/loading-and-then-dumping-an-omap-is-broken
для Python 3.7+, дикты сохраняют порядок вставки. Лучше всего использовать библиотеку, которая уважает это, например oyaml:
>>> 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'
основываясь на ответе @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
просто замените встроенную функцию, отсортированную по лямбда-функции, пока вы используете yaml.свалка.
один-лайнер, чтобы управлять ими всеми:
yaml.add_representer(dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))
вот и все. В конечном счете. После всех этих лет и часов, могучий 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:
чтобы предотвратить сортировку, нам просто нужно немного 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)