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
, s3
są kotwice na sekwencji (nie pokazane), które chcesz połączyć w nowej sekwencji, a następnie mieć d
, e
a 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.yaml
biblioteka 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, m1
to „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).