Questo non funzionerà:
l'unione è supportata solo dalle specifiche YAML per le mappature e non per le sequenze
stai mescolando completamente le cose avendo una chiave di unione <<
seguita dal separatore chiave / valore :
e un valore che è un riferimento e poi continui con un elenco allo stesso livello di rientro
Questo non è corretto YAML:
combine_stuff:
x: 1
- a
- b
Quindi la tua sintassi di esempio non avrebbe nemmeno senso come proposta di estensione YAML.
Se vuoi fare qualcosa come l'unione di più array potresti prendere in considerazione una sintassi come:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
dove s1
, s2
, s3
sono ancore sulle sequenze (non mostrate) che si desidera unire in una nuova sequenza e quindi avere la d
, e
e f
allegati a questo. Ma YAML sta risolvendo prima questo tipo di strutture in profondità, quindi non è disponibile un contesto reale durante l'elaborazione della chiave di unione. Non sono disponibili array / elenchi a cui allegare il valore elaborato (la sequenza ancorata).
Puoi adottare l'approccio proposto da @dreftymac, ma questo ha l'enorme svantaggio di dover in qualche modo sapere quali sequenze annidate appiattire (ovvero conoscendo il "percorso" dalla radice della struttura dati caricata alla sequenza genitore), o che si percorra ricorsivamente la struttura dati caricata alla ricerca di array / elenchi annidati e li si appiattisca indiscriminatamente.
Una soluzione migliore IMO sarebbe quella di utilizzare i tag per caricare strutture di dati che eseguono l'appiattimento per te. Ciò consente di indicare chiaramente cosa deve essere appiattito e cosa no e ti dà il pieno controllo sul fatto che questo appiattimento venga effettuato durante il caricamento o durante l'accesso. Quale scegliere è una questione di facilità di implementazione ed efficienza nel tempo e nello spazio di archiviazione. Questo è lo stesso compromesso che deve essere fatto per implementare la funzionalità chiave di unione e non esiste un'unica soluzione che sia sempre la migliore.
Ad esempio, la mia ruamel.yaml
libreria utilizza la forza bruta merge-dict durante il caricamento quando si utilizza il suo safe-loader, il che si traduce in dizionari uniti che sono normali dict di Python. Questa fusione deve essere eseguita in anticipo e duplica i dati (spazio inefficiente) ma è veloce nella ricerca del valore. Quando si utilizza il caricatore di andata e ritorno, si desidera essere in grado di eseguire il dump delle unioni non unite, quindi è necessario tenerle separate. Il dict come la struttura dati caricata come risultato del caricamento di andata e ritorno, è efficiente in termini di spazio ma più lento nell'accesso, poiché deve provare a cercare una chiave non trovata nel dict stesso nelle unioni (e questa non è memorizzata nella cache, quindi deve essere fatto ogni volta). Ovviamente tali considerazioni non sono molto importanti per file di configurazione relativamente piccoli.
Quanto segue implementa uno schema simile alla fusione per gli elenchi in Python usando oggetti con tag flatten
che ricorsero al volo in elementi che sono elenchi e contrassegnati toflatten
. Usando questi due tag puoi avere il file YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(l'uso di sequenze in stile flusso e blocco è completamente arbitrario e non ha alcuna influenza sul risultato caricato).
Quando si itera sugli elementi che sono il valore per la chiave, m1
questo "ricorre" nelle sequenze contrassegnate con toflatten
, ma visualizza altri elenchi (con alias o meno) come un singolo elemento.
Un modo possibile con il codice Python per ottenere ciò è:
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)
quali uscite:
1
2
[3, 4]
[5, 6]
7
8
Come puoi vedere puoi vedere, nella sequenza che deve essere appiattita, puoi usare un alias per una sequenza con tag oppure puoi usare una sequenza con tag. YAML non ti permette di fare:
- !flatten *x2
, vale a dire contrassegnare una sequenza ancorata, in quanto ciò la trasformerebbe essenzialmente in una struttura dati diversa.
L'uso di tag espliciti è IMO migliore che avere un po 'di magia come con le chiavi di unione YAML <<
. Se non altro ora devi passare attraverso i cerchi se ti capita di avere un file YAML con una mappatura che ha una chiave
<<
che non vuoi che si comporti come una chiave di unione, ad esempio quando fai una mappatura degli operatori C alle loro descrizioni in inglese (o qualche altra lingua naturale).