TL;DR: решение находится в двух строках с комментариями «СМОТРИТЕ ЗДЕСЬ!» Возможно работать с YAML как с dicts внутри вашей программы и с порядком в сохраненном файле/тексте, если вы согласитесь, что на выходе будут списки списков.
Если вы не возражаете против ужасно уродливых явных типов, таких как !!python/ordered_dict или !!omap, засоряющих ваш файл, вы также можете пойти по этому пути. Мой голос за !!omap, но я не уверен, сколько инструментов/библиотек поддерживают его (хотя я почти уверен, что меньше инструментов поддерживают !!python/ordered_dict). В конечном итоге вы имеете дело с двумя независимыми наборами данных: самим словарем и метаданными, определяющими порядок ключей.
(Существуют полуволшебные способы принуждения упорядоченного словаря в YAML без беспорядка !!python/ordered_dict или !!omap повсюду, но они хрупкие, противоречат самому определению словарей и, вероятно, сломаются по мере развития базовой библиотеки YAML. Кстати, эта ситуация идентична для JSON, поскольку YAML является надмножеством JSON и ни один из них не гарантирует порядок ключей, что означает, что обходные пути ломаются в первый раз, когда стандартный инструмент/пользователь искажает файл.)
Остальная часть этого поста представляет собой пример/проверочный код и объяснение того, почему все так.
from __future__ import print_function
import yaml
# Setting up some example data
d = {'name': 'A Project',
'version': {'major': 1, 'minor': 4, 'patch': 2},
'add-ons': ['foo', 'bar', 'baz']}
# LOOK HERE!
ordering = ['name', 'version', 'add-ons', 'papayas']
ordered_set = [[x, d[x]] for x in ordering if x in d.keys()]
# In the event you only care about a few keys,
# you can tack the unspecified ones onto the end
# Note that 'papayas' isn't a key. You can establish an ordering that
# includes optional keys by using 'if' as a guard in the list comprehension.
# Demonstration
things = {'unordered.yaml': d, 'ordered.yaml': ordered_set}
for k in things:
f = open(k, 'w')
f.write(yaml.dump(things[k], default_flow_style=False, allow_unicode=True))
f.close()
# Let's check the result
output = []
for k in things:
f = open(k, 'r')
output.append(dict(yaml.load(f.read())))
f.close()
# Should print 'OK'
if output[0] == output[1]:
print('OK')
else:
print('Something is wrong')
Созданные файлы выглядят так:
заказал.yaml:
- - name
- A Project
- - version
- major: 1
minor: 4
patch: 2
- - add-ons
- - foo
- bar
- baz
неупорядоченный.yaml:
add-ons:
- foo
- bar
- baz
name: A Project
version:
major: 1
minor: 4
patch: 2
Это не дает такого красивого документа YAML, как вы могли бы надеяться. Тем не менее, он может принимать красивый YAML в качестве начального ввода (ура!), а создание сценария преобразования из некрасивого, упорядоченного YAML в красивый, все еще упорядоченный YAML в стиле dict является простым (это я оставляю вам как упражнение) .
Если у вас есть порядок ключей, который вы хотите сохранить, запишите его в упорядоченный список/кортеж. Используйте этот список для создания упорядоченного списка списков (но не списка кортежей, потому что вы получите тип !!python/tuple в YAML, и это отстой). Сбросить это в YAML. Чтобы прочитать его обратно, прочитайте его как обычно, затем передайте эту структуру в dict(), и вы вернетесь к исходному словарю, с которого вы начали. Возможно, вам придется рекурсивно спуститься по структуре, если у вас есть вложенная структура, требующая сохранения ее порядка (это легче сделать в коде, чем объяснять прозой — это то, что вы, вероятно, уже знаете).
В этом примере я хочу, чтобы «имя» проекта стояло первым в файле, затем элементы номера «версии», а затем «дополнения». Обычно PyYAML упорядочивает словарные ключи в алфавитно-цифровом порядке, когда вы вызываете dump(), но это ненадежно, потому что это может измениться в будущем, и в стандарте YAML нет ничего, что требовало бы этого, поэтому я не могу гарантировать, что другая утилита YAML будет делать вещи таким образом. «дополнения» стоят перед «именем», поэтому у меня проблема с заказом. Итак, я определяю свой порядок, затем упаковываю упорядоченный список списков, а затем выгружаю его.
Вы просите порядка из чего-то, что по своей сути неупорядочено. Словарь — это хэш-таблица, упорядоченная внутри исключительно для скорости поиска. С этим порядком вы не должны иметь ничего общего, потому что, если завтра будет обнаружен более быстрый способ реализации словарей, среда выполнения должна реализовать его, не нарушая код каждого, который зависел от словарей, являющихся полезной абстракцией хеш-таблицы.
Точно так же YAML — это не язык разметки (в конце концов, изначально он расшифровывался как «Yaml — это не язык разметки»), это формат данных. Разница важна. Некоторые данные упорядочены, например кортежи и списки; некоторые - нет, например пакеты пар ключ-значение (немного отличаются от хэш-таблицы, но концептуально похожи).
Я использую рекурсивную версию такого решения, чтобы гарантировать вывод YAML в разных реализациях YAML, не для удобочитаемости, а потому, что я передаю много данных в YAML, и каждая запись должна быть подписана ключом, а неопределенный порядок предотвращает единые подписи всякий раз, когда используются диктофоны/хэши.
person
zxq9
schedule
12.03.2014
{'3':5, '1':3}
в консоли. Словари Python неупорядочены. Таким образом, ваша первая задача — решить, как вы хотите изменить свои структуры данных, если хотите сохранить порядок (будь то список кортежей,collections.OrderedDict
или что-то еще). - person DSM   schedule 10.01.2014json.dump
поддержкаcollections.OrderedDict
. И я обнаружил, чтоjson.loads
может загружатьdict
какcollections.OrderedDict
с помощью хука. См. здесь Могу ли я загрузить JSON в OrderedDict в Python?. - person Kei Minagawa   schedule 12.03.2014