Формат вывода YAML для python

Я использую PyYaml для вывода файла YAML. Но он переупорядочивает мои товары. лайк

>>> yaml.dump({'3':5, '1':3})
"{'1': 3, '3': 5}\n"

Я хочу получить "{'3': 5, '1': 3}\n". Могу ли я сделать это

ПС. Я пробовал collections.OrderedDict. Выход нехороший. Нравится подписаться

>>> a= collections.OrderedDict()
>>> a['3']=1
>>> a['1']=2
>>> a['5']=2
>>> yaml.dump(a)
"!!python/object/apply:collections.OrderedDict\n- - ['3', 1]\n  - ['1', 2]\n  - ['5', 2]\n"

person Samuel    schedule 10.01.2014    source источник
comment
Словари Python неупорядочены.   -  person Ashwini Chaudhary    schedule 10.01.2014
comment
Это не имеет ничего общего с YAML, но с Python: попробуйте ввести {'3':5, '1':3} в консоли. Словари Python неупорядочены. Таким образом, ваша первая задача — решить, как вы хотите изменить свои структуры данных, если хотите сохранить порядок (будь то список кортежей, collections.OrderedDict или что-то еще).   -  person DSM    schedule 10.01.2014
comment
@DSM Спасибо. Но он добавляет !!python/objectapply:collections.OrderedDict в мой файл YAML.   -  person Samuel    schedule 10.01.2014
comment
есть ли какая-то конкретная причина ожидать этого результата?   -  person fang    schedule 10.01.2014
comment
@ Хай Фан Да. Я хочу, чтобы мой файл YAML выводил элемент в определенном порядке   -  person Samuel    schedule 10.01.2014
comment
Взгляните на stackoverflow.com/questions/5121931/   -  person Nigel Tufnel    schedule 10.01.2014
comment
Если вам не нужно использовать YAML, рассмотрите возможность использования JSON. json.dump поддержка collections.OrderedDict. И я обнаружил, что json.loads может загружать dict как collections.OrderedDict с помощью хука. См. здесь Могу ли я загрузить JSON в OrderedDict в Python?.   -  person Kei Minagawa    schedule 12.03.2014


Ответы (2)


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

Отображения YAML неупорядочены, как и словари Python. Официальный способ чтения файла и сохранения порядка — использовать !!omap, но они преобразуются в кортежи в PyYAML и их не так просто обновить, как dict/ordereddict/OrderedDict.

Если у вас уже есть файл yaml, который вы читаете и обновляете, вы можете использовать мой ruamel.yaml библиотека, которая считывает сопоставления при использовании в режиме приема-передачи как упорядоченный словарь и записывает их как обычные сопоставления (а также сохраняет комментарии).

В качестве ответа на другой вопрос был приведен пример использования.

person Anthon    schedule 24.11.2014