Я хотел бы, чтобы загрузчик PyYAML загружал сопоставления (и упорядоченные сопоставления) в тип Python 2.7+ OrderedDict вместо ванили dict
и списка пар, которые он сейчас использует.
Как лучше всего это сделать?
источник
Я хотел бы, чтобы загрузчик PyYAML загружал сопоставления (и упорядоченные сопоставления) в тип Python 2.7+ OrderedDict вместо ванили dict
и списка пар, которые он сейчас использует.
Как лучше всего это сделать?
Обновление: в python 3.6+ вам, вероятно, вообще не нужно OrderedDict
из-за новой реализации dict , которая некоторое время использовалась в pypy (хотя на данный момент рассматривается деталь реализации CPython).
Обновление: в Python 3.7+ характер сохранения порядка вставки объектов dict был объявлен официальной частью спецификации языка Python , см. Что нового в Python 3.7 .
Мне нравится решение @James за его простоту. Однако он изменяет глобальный yaml.Loader
класс по умолчанию , что может привести к неприятным побочным эффектам. Особенно при написании кода библиотеки это плохая идея. Кроме того, он не работает напрямую с yaml.safe_load()
.
К счастью, решение можно улучшить без особых усилий:
import yaml
from collections import OrderedDict
def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
class OrderedLoader(Loader):
pass
def construct_mapping(loader, node):
loader.flatten_mapping(node)
return object_pairs_hook(loader.construct_pairs(node))
OrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_mapping)
return yaml.load(stream, OrderedLoader)
# usage example:
ordered_load(stream, yaml.SafeLoader)
Для сериализации я не знаю очевидного обобщения, но, по крайней мере, у этого не должно быть никаких побочных эффектов:
def ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds):
class OrderedDumper(Dumper):
pass
def _dict_representer(dumper, data):
return dumper.represent_mapping(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
data.items())
OrderedDumper.add_representer(OrderedDict, _dict_representer)
return yaml.dump(data, stream, OrderedDumper, **kwds)
# usage:
ordered_dump(data, Dumper=yaml.SafeDumper)
Модуль yaml позволяет вам указывать настраиваемых «репрезентаторов» для преобразования объектов Python в текст и «конструкторов» для обратного процесса.
источник
from six import iteritems
а затем измените его наiteritems(data)
так, чтобы он одинаково хорошо работал в Python 2 и 3.represent_dict
иDEFAULT_MAPPING_TAG
). Это потому, что документация неполная, или эти функции не поддерживаются и могут быть изменены без предварительного уведомления?dict_constructor
вам нужно позвонить,loader.flatten_mapping(node)
иначе вы не сможете загрузить<<: *...
(синтаксис слияния)Вариант 2018:
oyaml
является заменой PyYAML, которая сохраняет порядок слов. Поддерживаются как Python 2, так и Python 3. Простоpip install oyaml
и импортируйте, как показано ниже:Вас больше не будут раздражать ошибочные сопоставления при сбросе / загрузке.
Примечание: я автор oyaml.
источник
Вариант 2015 года (и позже):
ruamel.yaml - это замена PyYAML (отказ от ответственности: я являюсь автором этого пакета). Сохранение порядка сопоставлений было одной из вещей, добавленных в первой версии (0.1) еще в 2015 году. Это не только сохраняет порядок ваших словарей, но также сохраняет комментарии, имена привязок, теги и поддерживает YAML 1.2. спецификация (выпущена в 2009 г.)
В спецификации говорится, что порядок не гарантируется, но, конечно, в файле YAML есть порядок, и соответствующий синтаксический анализатор может просто сохранить его и прозрачно сгенерировать объект, который сохраняет порядок. Вам просто нужно правильно выбрать парсер, загрузчик и дампер¹:
дам тебе:
data
относится к типу,CommentedMap
который функционирует как dict, но имеет дополнительную информацию, которая хранится до тех пор, пока не будет сброшена (включая сохраненный комментарий!)источник
CommentedMap
напрямую, но это не работает иOrderedDict
ставит!!omap
везде, что не очень удобно.CommentedMap
помощьюsafe=True
inYAML
, но это не сработало (с использованиемsafe=False
работает). У меня также была проблема сCommentedMap
невозможностью изменения, но я не могу воспроизвести ее сейчас ... Я открою новый вопрос, если снова столкнусь с этой проблемой.yaml = YAML()
, вы получаете парсер / дампер туда и обратно, и это является производным от безопасного парсера / дампера, который знает о CommentedMap / Seq и т. Д.Примечание : существует библиотека, основанная на следующем ответе, которая также реализует CLoader и CDumpers: Phynix / yamlloader
Я очень сомневаюсь, что это лучший способ сделать это, но это то, что я придумал, и он действительно работает. Также доступно как суть .
источник
key_node.start_mark
атрибут в свое сообщение об ошибке, я не вижу очевидного способа упростить ваш центральный цикл построения. Если вы попытаетесь использовать тот факт, чтоOrderedDict
конструктор будет принимать итерацию пар ключ-значение, вы потеряете доступ к этой детали при создании сообщения об ошибке.add_constructor
в своем__init__
методе.Обновление : библиотека устарела в пользу yamlloader (который основан на yamlordereddictloader)
Я только что нашел библиотеку Python ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ), которая была создана на основе ответов на этот вопрос и довольно проста в использовании:
источник
yodl
на github.В моей установке For PyYaml для Python 2.7 я обновил __init__.py, constructor.py и loader.py. Теперь поддерживает параметр object_pairs_hook для команд загрузки. Ниже приводится описание внесенных мною изменений.
источник
вот простое решение, которое также проверяет дублированные ключи верхнего уровня на вашей карте.
источник