Список YAML → генератор Python?

59
7

Мне было интересно, есть ли простой способ проанализировать документ YAML, состоящий из списка элементов в качестве генератора питона с использованием PyYAML.

Например, учитывая файл

# foobar.yaml
---
- foo: ["bar", "baz", "bah"]
something_else: blah
- bar: yet_another_thing

Я хотел бы иметь возможность сделать что-то вроде

for item in yaml.load_as_generator(open('foobar.yaml')): # does not exist
print(str(item))

Я знаю, что есть yaml.load_all, который может достичь аналогичной функциональности, но тогда вам нужно рассматривать каждую запись как свой собственный документ. Причина, по которой я спрашиваю, состоит в том, что у меня есть действительно большие файлы, которые я бы хотел преобразовать в YAML, а затем проанализировать с низким объемом памяти.

Я взглянул на API событий PyYAML, но он испугал меня =)

спросил(а) 2020-03-05T15:04:37+03:00 9 месяцев назад
1
Решение
58

Я понимаю, что API событий пугает вас, и это принесет вам столько всего. Прежде всего вам нужно будет отслеживать глубину (потому что у вас есть элементы сложной последовательности верхнего уровня, а также "бары", "баз" и т.д. И, правильно отрезав элементы событий последовательности нижнего уровня, вам нужно будет кормить их в композитор для создания узлов (и в конечном итоге объектов Python), а не тривиальных.

Но поскольку YAML использует отступы, даже для скаляров, охватывающих несколько строк, вы можете использовать простой синтаксический анализатор на основе строк, который распознает, где начинается каждый элемент последовательности, и подавать их в функцию normal load() одному за раз:

#/usr/bin/env python

import ruamel.yaml

def list_elements(fp, depth=0):
buffer = None
in_header = True
list_element_match = ' ' * depth + '- '
for line in fp:
if line.startswith('---'):
in_header = False
continue
if in_header:
continue
if line.startswith(list_element_match):
if buffer is None:
buffer = line
continue
yield ruamel.yaml.load(buffer)[0]
buffer = line
continue
buffer += line
if buffer:
yield ruamel.yaml.load(buffer)[0]

with open("foobar.yaml") as fp:
for element in list_elements(fp):
print(str(element))

в результате чего:

{'something_else': 'blah', 'foo': ['bar', 'baz', 'bah']}
{'bar': 'yet_another_thing'}

Я использовал расширенную версию PyYAML, ruamel.yaml здесь (из которых я являюсь автором), но PyYAML должен работать таким же образом.

ответил(а) 2020-03-05T15:20:37.048958+03:00 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема