Это не сработает:
слияние поддерживается только спецификациями YAML для сопоставлений, но не для последовательностей
вы полностью смешиваете вещи, имея ключ слияния, <<
за которым следует разделитель ключ / значение :
и значение, которое является ссылкой, а затем продолжаете со списком на том же уровне отступа
Это не правильный YAML:
combine_stuff:
x: 1
- a
- b
Таким образом, ваш пример синтаксиса даже не имеет смысла в качестве предложения по расширению YAML.
Если вы хотите сделать что-то вроде объединения нескольких массивов, вы можете рассмотреть такой синтаксис, как:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
где s1
, s2
, s3
являются якорями на последовательностях (не показаны) , которые вы хотите объединить в новую последовательность и затем иметь d
, e
и f
приложенные к этому. Но YAML сначала определяет глубину такого рода структур, поэтому во время обработки ключа слияния нет реального контекста. У вас нет доступного массива / списка, к которому вы могли бы прикрепить обработанное значение (закрепленную последовательность).
Вы можете использовать подход, предложенный @dreftymac, но это имеет огромный недостаток, заключающийся в том, что вам нужно каким-то образом знать, какие вложенные последовательности нужно сгладить (т.е. зная «путь» от корня загруженной структуры данных до родительской последовательности), или что вы рекурсивно просматриваете загруженную структуру данных в поисках вложенных массивов / списков и без разбора сглаживаете их все.
Лучшим решением IMO было бы использовать теги для загрузки структур данных, которые выполняют сглаживание за вас. Это позволяет четко обозначать, что нужно развернуть, а что нет, и дает вам полный контроль над тем, выполняется ли это выравнивание во время загрузки или во время доступа. Какой из них выбрать, зависит от простоты реализации и эффективности использования времени и места для хранения. Это тот же компромисс, который необходимо сделать для реализации ключевой функции слияния, и не существует единого решения, которое всегда было бы лучшим.
Например, моя ruamel.yaml
библиотека использует слияния грубой силы во время загрузки при использовании своего безопасного загрузчика, что приводит к объединению словарей, которые являются обычными dicts Python. Это слияние должно выполняться заранее и дублирует данные (неэффективное использование пространства), но при этом выполняется быстрый поиск значений. При использовании загрузчика туда и обратно вы хотите иметь возможность выгружать слияния без объединения, поэтому их нужно хранить отдельно. Такая структура данных, как dict, загруженная в результате загрузки туда и обратно, занимает мало места, но имеет более медленный доступ, поскольку ему нужно попытаться найти ключ, не найденный в самом dict при слияниях (и он не кэшируется, поэтому он нужно делать каждый раз). Конечно, такие соображения не очень важны для относительно небольших файлов конфигурации.
Следующее реализует схему слияния для списков в Python с использованием объектов с тегом, flatten
которые на лету рекурсируют в элементы, которые являются списками и помечены toflatten
. Используя эти два тега, вы можете получить файл YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(использование последовательностей стилей потока и блока совершенно произвольно и не влияет на загруженный результат).
При итерации по элементам, которые являются значением для ключа, m1
это "рекурсивно" превращается в последовательности, помеченныеtoflatten
, но отображает другие списки (с псевдонимами или без них) как один элемент.
Один из возможных способов достижения этого с помощью кода Python:
import sys
from pathlib import Path
import ruamel.yaml
yaml = ruamel.yaml.YAML()
@yaml.register_class
class Flatten(list):
yaml_tag = u'!flatten'
def __init__(self, *args):
self.items = args
@classmethod
def from_yaml(cls, constructor, node):
x = cls(*constructor.construct_sequence(node, deep=True))
return x
def __iter__(self):
for item in self.items:
if isinstance(item, ToFlatten):
for nested_item in item:
yield nested_item
else:
yield item
@yaml.register_class
class ToFlatten(list):
yaml_tag = u'!toflatten'
@classmethod
def from_yaml(cls, constructor, node):
x = cls(constructor.construct_sequence(node, deep=True))
return x
data = yaml.load(Path('input.yaml'))
for item in data['m1']:
print(item)
который выводит:
1
2
[3, 4]
[5, 6]
7
8
Как видите, вы можете видеть, что в последовательности, которая требует выравнивания, вы можете использовать псевдоним для помеченной последовательности или вы можете использовать помеченную последовательность. YAML не позволяет делать:
- !flatten *x2
, т. е. пометить закрепленную последовательность, так как это существенно превратит ее в другую структуру данных.
Использование явных тегов - это IMO лучше, чем какая-то магия, как с ключами слияния YAML <<
. Если ничего другого, вам теперь придется пройти через обручи, если у вас есть файл YAML с сопоставлением, у которого есть ключ,
<<
который вы не хотите действовать как ключ слияния, например, когда вы делаете сопоставление операторов C с их описаниями на английском (или другом естественном языке).