Скопируйте псевдонимы для отдельных объектов Python при загрузке YAML с помощью ruamel.yaml

При загрузке данных YAML с помощью ruamel.yaml якоря и их псевдонимы в файле YAML являются одним и тем же объектом в Python:

from ruamel.yaml import YAML

yaml_str = """\
first: &reference [1, 2, 3]
second: *reference
"""

yaml = YAML()
data = yaml.load(yaml_str)

assert(data['first'] is data['second'])
# passes

data['first'].append(4)
print(data['second'])
# output: [1, 2, 3, 4]

Я понимаю, что это преднамеренная функция. Однако есть ли способ заставить load вместо этого копировать псевдонимы, когда он их найдет? Я попытался переопределить yaml.representer.ignore_aliases, как указано в этом ответе, но это только для записи в YAML, а не для чтения из него.


person CrazyChucky    schedule 24.12.2020    source источник


Ответы (1)


Нет встроенной функциональности, чтобы делать то, что вы хотите. Каждый раз, когда вы сталкиваетесь с псевдонимом, вам придется рекурсивно создавать структуру узла в компоновщике, а не просто возвращать узел привязки для псевдонима.

Необходимость в якорях и псевдонимах в документах YAML возникает из-за необходимости иметь возможность представлять рекурсивные структуры данных:

a = [1, 2]
a.append(a)

Вышеупомянутая структура не может быть сброшена с использованием метода, показанного в ответе, на который вы ссылаетесь, и также не может быть представлено в виде документа YAML без расширения:

&id001
- 1
- 2
- *id001

расширяться во время загрузки по методике, предложенной в первом абзаце.

Ваш (нерекурсивный) пример можно сбросить с помощью метода, указанного в связанном ответе, а также его можно расширить во время загрузки.

Самое простое решение, если вы не хотите погружаться в недра ruamel.yaml, — это загрузить свой документ YAML, сделать дамп с расширением, а затем загрузить результат дампа:

import sys
import ruamel.yaml

yaml_str = """\
first: &reference [1, 2, 3]
second: *reference
"""

yaml = ruamel.yaml.YAML()
yaml.representer.ignore_aliases = lambda *data: True
data = yaml.load(yaml_str)
buf = ruamel.yaml.compat.BytesIO()
out = yaml.dump(data, buf)
data = yaml.load(buf.getvalue())


assert(data['first'] is not data['second'])
assert(data['first'] == data['second'])

data['first'].append(4)
assert len(data['first']) == 4
assert len(data['second']) == 3

Конечно, это не очень эффективно, когда у вас огромный файл.

person Anthon    schedule 24.12.2020
comment
Спасибо, это имеет смысл! Это кажется немного хакерским, но для достаточно небольших файлов это вполне работоспособное решение, которое даже не пришло мне в голову. - person CrazyChucky; 24.12.2020