Подтвердить что ты не робот

Форматирование вывода дампа PyYAML()

У меня есть список словарей, которые я хочу сериализовать:

list_of_dicts = [ { 'key_1': 'value_a', 'key_2': 'value_b'},
                  { 'key_1': 'value_c', 'key_2': 'value_d'},
                  ...
                  { 'key_1': 'value_x', 'key_2': 'value_y'}  ]

yaml.dump(list_of_dicts, file, default_flow_style = False)

выдает следующее:

- key_1: value_a
  key_2: value_b
- key_1: value_c
  key_2: value_d
(...)
- key_1: value_x
  key_2: value_y

Но я хотел бы получить следующее:

- key_1: value_a
  key_2: value_b
                     <-|
- key_1: value_c       | 
  key_2: value_d       |  empty lines between blocks
(...)                  |
                     <-|
- key_1: value_x
  key_2: value_y

PyYAML documentation говорит о аргументах dump() очень кратко и, похоже, ничего не имеет на эту тему.

Редактирование файла вручную для добавления новых строк улучшает читаемость довольно много, и структура по-прежнему загружается сразу после этого, но я понятия не имею, как создать метод дампа.

И вообще, есть ли способ иметь больший контроль над форматированием вывода, кроме простого отступа?

4b9b3361

Ответ 1

Нет простого способа сделать это с помощью библиотеки (Node объекты в дереве синтаксиса дампера yaml пассивны и не могут испускать эту информацию), поэтому я закончил с

stream = yaml.dump(list_of_dicts, default_flow_style = False)
file.write(stream.replace('\n- ', '\n\n- '))

Ответ 2

Документация PyYAML кратко говорит о аргументах dump(), потому что говорить нечего. Этот вид управления не предоставляется PyYAML.

Чтобы обеспечить сохранение таких пустых (и комментариев) строк в загружаемом YAML, я начал разработку библиотеки ruamel.yaml, надмножество застопоренного PyYAML с совместимостью с YAML 1.2, добавлено много функций и исправлены ошибки. С помощью ruamel.yaml вы можете:

import sys
import ruamel.yaml

yaml_str = """\
- key_1: value_a
  key_2: value_b

- key_1: value_c
  key_2: value_d

- key_1: value_x  # a few before this were ellipsed
  key_2: value_y
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

и получить вывод точно так же, как и входная строка (включая комментарий).

Вы также можете создать вывод, который вы хотите с нуля:

import sys
import ruamel.yaml

yaml = ruamel.yaml.YAML()
list_of_dicts = yaml.seq([ { 'key_1': 'value_a', 'key_2': 'value_b'},
                           { 'key_1': 'value_c', 'key_2': 'value_d'},
                           { 'key_1': 'value_x', 'key_2': 'value_y'}  ])

for idx in range(1, len(list_of_dicts)):
    list_of_dicts.yaml_set_comment_before_after_key(idx, before='\n')

ruamel.yaml.comments.dump_comments(list_of_dicts)
yaml.dump(list_of_dicts, sys.stdout)

Преобразование с использованием yaml.seq() необходимо для создания объекта, который позволяет привязывать пустые строки к специальным атрибутам.

Библиотека также позволяет сохранять/легко устанавливать кавычки и буквенный стиль в строках, формат int (шестнадцатеричный, восьмеричный, двоичный) и поплавки. А также отдельная спецификация отступа для сопоставлений и последовательностей (хотя и не для отдельных отображений или последовательностей).

Ответ 3

В то время как его маленький klunky, я имел ту же цель, что и OP. Я решил это путем подкласса yaml.Dumper

from yaml import Dumper

class MyDumper(Dumper):

  def write_indent(self):
    indent = self.indent or 0
    if not self.indention or self.column > indent \
        or (self.column == indent and not self.whitespace):
      self.write_line_break()


    ##########$#######################################
    # On the first level of indentation, add an extra
    # newline

    if indent == 2:
      self.write_line_break()

    ##################################################

    if self.column < indent:
      self.whitespace = True
      data = u' '*(indent-self.column)
      self.column = indent
      if self.encoding:
        data = data.encode(self.encoding)
      self.stream.write(data)

Вы называете это следующим образом:

print dump(python_dict, default_flow_style=False, width=79, Dumper=MyDumper)