Das wird nicht funktionieren:
Das Zusammenführen wird nur von den YAML-Spezifikationen für Zuordnungen und nicht für Sequenzen unterstützt
Sie mischen die Dinge vollständig, indem Sie einen Zusammenführungsschlüssel <<
gefolgt vom Schlüssel- / Werttrennzeichen :
und einem Wert als Referenz verwenden und dann mit einer Liste auf derselben Einrückungsstufe fortfahren
Dies ist nicht korrekt YAML:
combine_stuff:
x: 1
- a
- b
Ihre Beispielsyntax wäre also als YAML-Erweiterungsvorschlag nicht einmal sinnvoll.
Wenn Sie beispielsweise mehrere Arrays zusammenführen möchten, sollten Sie eine Syntax wie die folgende in Betracht ziehen:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
wo s1
, s2
, s3
sind Anker auf Sequenzen (nicht dargestellt) , dass Sie in eine neue Sequenz zu fusionieren wollen und haben dann die d
, e
und f
der angehängt. YAML löst diese Art von Strukturtiefe jedoch zuerst auf, sodass während der Verarbeitung des Zusammenführungsschlüssels kein realer Kontext verfügbar ist. Es steht Ihnen kein Array / keine Liste zur Verfügung, an die Sie den verarbeiteten Wert (die verankerte Sequenz) anhängen könnten.
Sie können den von @dreftymac vorgeschlagenen Ansatz wählen, dies hat jedoch den großen Nachteil, dass Sie irgendwie wissen müssen, welche verschachtelten Sequenzen abgeflacht werden sollen (dh indem Sie den "Pfad" von der Wurzel der geladenen Datenstruktur zur übergeordneten Sequenz kennen). oder dass Sie die geladene Datenstruktur rekursiv durchsuchen, nach verschachtelten Arrays / Listen suchen und diese wahllos reduzieren.
Eine bessere Lösung IMO wäre die Verwendung von Tags zum Laden von Datenstrukturen, die die Abflachung für Sie übernehmen. Auf diese Weise können Sie klar angeben, was abgeflacht werden muss und was nicht, und Sie haben die volle Kontrolle darüber, ob diese Abflachung während des Ladens oder während des Zugriffs erfolgt. Welche Sie wählen sollten, hängt von der einfachen Implementierung und der Effizienz in Bezug auf Zeit und Speicherplatz ab. Dies ist der gleiche Kompromiss , dass der Bedarf für die Umsetzung des gemacht wird merge Schlüsselmerkmal , und es gibt keine einheitliche Lösung , die immer die beste ist.
Beispielsweise verwendet meine ruamel.yaml
Bibliothek beim Laden die Brute-Force-Merge-Dicts, wenn sie ihren Safe-Loader verwendet. Dies führt zu zusammengeführten Wörterbüchern, die normale Python-Dicts sind. Diese Zusammenführung muss im Voraus erfolgen und dupliziert Daten (Speicherplatz ineffizient), ist jedoch bei der Wertsuche schnell. Wenn Sie den Roundtrip-Loader verwenden, möchten Sie in der Lage sein, die Zusammenführungen nicht zusammengeführt zu speichern, sodass sie getrennt aufbewahrt werden müssen. Das Diktat wie eine Datenstruktur, die als Ergebnis des Round-Trip-Ladens geladen wird, ist platzsparend, aber langsamer im Zugriff, da versucht werden muss, einen Schlüssel zu suchen, der im Diktat selbst in den Zusammenführungen nicht enthalten ist (und dieser wird nicht zwischengespeichert, also ist es so) muss jedes Mal gemacht werden). Natürlich sind solche Überlegungen für relativ kleine Konfigurationsdateien nicht sehr wichtig.
Im Folgenden wird ein zusammenführungsähnliches Schema für Listen in Python implementiert, bei dem Objekte mit einem Tag verwendet werden, flatten
das im laufenden Betrieb in Elemente umgewandelt wird, die Listen und Tags sind toflatten
. Mit diesen beiden Tags können Sie eine YAML-Datei haben:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(Die Verwendung von Flow- und Block-Style-Sequenzen ist völlig willkürlich und hat keinen Einfluss auf das geladene Ergebnis.)
Beim Durchlaufen der Elemente, die den Wert für den Schlüssel darstellen, m1
"rekursiert" dies in die mit Tags gekennzeichneten Sequenzen toflatten
, zeigt jedoch andere Listen (mit oder ohne Alias) als einzelnes Element an.
Ein möglicher Weg, dies mit Python-Code zu erreichen, ist:
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)
welche Ausgänge:
1
2
[3, 4]
[5, 6]
7
8
Wie Sie sehen können, können Sie in der Sequenz, die abgeflacht werden muss, entweder einen Alias für eine markierte Sequenz oder eine markierte Sequenz verwenden. YAML erlaubt dir nicht:
- !flatten *x2
, dh eine verankerte Sequenz markieren, da dies im Wesentlichen zu einer anderen Datenstruktur führen würde.
Die Verwendung expliziter Tags ist IMO besser als etwas Magie wie bei YAML-Zusammenführungsschlüsseln <<
. Wenn Sie eine YAML-Datei mit einer Zuordnung haben, die einen Schlüssel enthält <<
, den Sie nicht wie einen Zusammenführungsschlüssel verhalten möchten, müssen Sie jetzt die Rahmen durchlaufen
, z. B. wenn Sie eine Zuordnung von C-Operatoren zu ihren Beschreibungen vornehmen in Englisch (oder einer anderen natürlichen Sprache).