To nie zadziała:
scalanie jest obsługiwane tylko przez specyfikacje YAML dla mapowań, a nie dla sekwencji
całkowicie mieszasz rzeczy, mając klucz scalania, <<
po którym następuje separator klucz / wartość :i wartość, która jest odwołaniem, a następnie kontynuujesz pracę z listą na tym samym poziomie wcięcia
To nie jest poprawne YAML:
combine_stuff:
x: 1
- a
- b
Więc twoja przykładowa składnia nie miałaby nawet sensu jako propozycja rozszerzenia YAML.
Jeśli chcesz zrobić coś takiego, jak scalanie wielu tablic, możesz rozważyć składnię taką jak:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
gdzie s1, s2, s3są kotwice na sekwencji (nie pokazane), które chcesz połączyć w nowej sekwencji, a następnie mieć d, ea f
załączone do tego. Ale YAML najpierw rozwiązuje ten rodzaj głębi struktur, więc podczas przetwarzania klucza scalającego nie jest dostępny prawdziwy kontekst. Nie ma dostępnej tablicy / listy, do której można by dołączyć przetworzoną wartość (zakotwiczoną sekwencję).
Możesz przyjąć podejście zaproponowane przez @dreftymac, ale ma to ogromną wadę, że w jakiś sposób musisz wiedzieć, które zagnieżdżone sekwencje spłaszczyć (tj. Znając "ścieżkę" od korzenia załadowanej struktury danych do sekwencji nadrzędnej), lub że rekurencyjnie przechodzisz po załadowanej strukturze danych, szukając zagnieżdżonych tablic / list i bezkrytycznie spłaszczasz je wszystkie.
Lepszym rozwiązaniem IMO byłoby użycie tagów do ładowania struktur danych, które wykonują spłaszczanie za Ciebie. Pozwala to na wyraźne oznaczenie tego, co należy spłaszczyć, a co nie oraz daje pełną kontrolę nad tym, czy spłaszczanie to odbywa się podczas ładowania, czy podczas dostępu. To, który z nich wybrać, to kwestia łatwości wdrożenia oraz wydajności w zakresie czasu i przestrzeni magazynowej. Jest to ten sam kompromis, który musi być wykonany za wdrożenie seryjnej kluczową cechę i nie ma jednego rozwiązania, które jest zawsze najlepszy.
Np. Moja ruamel.yamlbiblioteka używa dyktowania typu brute force merge-dicts podczas ładowania, kiedy używa swojego bezpiecznego programu ładującego, co skutkuje połączonymi słownikami, które są normalnymi dyktami Pythona. To scalanie musi być wykonane z góry i powiela dane (nieefektywne pod względem miejsca), ale jest szybkie w wyszukiwaniu wartości. Korzystając z modułu ładującego w obie strony, chcesz mieć możliwość zrzucania połączeń nierozłącznych, więc należy je przechowywać oddzielnie. Dykt, podobnie jak struktura danych załadowana w wyniku ładowania w obie strony, zajmuje mało miejsca, ale jest wolniejszy w dostępie, ponieważ musi spróbować wyszukać klucz, którego nie znaleziono w samym dyktowaniu w połączeniach (i to nie jest buforowane, więc za każdym razem). Oczywiście takie względy nie są bardzo ważne w przypadku stosunkowo małych plików konfiguracyjnych.
Poniższy schemat implementuje schemat scalania dla list w Pythonie przy użyciu obiektów ze znacznikami, flatten
które w locie powracają do elementów, które są listami i oznaczane toflatten. Używając tych dwóch tagów możesz mieć plik YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(użycie sekwencji typu flow vs block jest całkowicie dowolne i nie ma wpływu na ładowany wynik).
Podczas iteracji po elementach, które są wartością klucza, m1to „powraca” do sekwencji oznaczonych tagiem toflatten, ale wyświetla inne listy (z aliasami lub bez) jako pojedynczy element.
Jednym z możliwych sposobów osiągnięcia tego celu za pomocą kodu Pythona jest:
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)
które wyjścia:
1
2
[3, 4]
[5, 6]
7
8
Jak widać, w sekwencji, która wymaga spłaszczenia, możesz użyć aliasu do sekwencji ze znacznikiem lub możesz użyć sekwencji ze znacznikiem. YAML nie pozwala na:
- !flatten *x2
, tj. oznaczyć zakotwiczoną sekwencję, ponieważ zasadniczo uczyniłoby to inną strukturę danych.
Używanie znaczników jawnych jest lepsze w IMO niż magia, jak w przypadku kluczy scalających YAML <<. Jeśli nic innego, musisz teraz przechodzić przez obręcze, jeśli masz plik YAML z mapowaniem, który ma klucz
<<, którego nie chcesz zachowywać się jak klucz scalający, np. Kiedy wykonujesz mapowanie operatorów C do ich opisów w języku angielskim (lub innym języku naturalnym).