Esto no va a funcionar:
la combinación solo es compatible con las especificaciones YAML para asignaciones y no para secuencias
está mezclando completamente las cosas al tener una clave de combinación <<
seguida del separador de clave / valor :
y un valor que es una referencia y luego continúa con una lista en el mismo nivel de sangría
Esto no es correcto YAML:
combine_stuff:
x: 1
- a
- b
Por lo tanto, su sintaxis de ejemplo ni siquiera tendría sentido como propuesta de extensión YAML.
Si desea hacer algo como fusionar varias matrices, es posible que desee considerar una sintaxis como:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
donde s1
, s2
, s3
son anclas en secuencias (no mostrados) que desea combinar en una nueva secuencia y luego tener el d
, e
yf
adjuntas a eso. Pero YAML primero está resolviendo este tipo de estructuras en profundidad, por lo que no hay un contexto real disponible durante el procesamiento de la clave de combinación. No hay una matriz / lista disponible a la que pueda adjuntar el valor procesado (la secuencia anclada).
Puede adoptar el enfoque propuesto por @dreftymac, pero esto tiene la gran desventaja de que de alguna manera necesita saber qué secuencias anidadas aplanar (es decir, conociendo la "ruta" desde la raíz de la estructura de datos cargada hasta la secuencia principal), o que recorra de forma recursiva la estructura de datos cargada en busca de matrices / listas anidadas y las aplana todas indiscriminadamente.
En mi opinión, una mejor solución sería usar etiquetas para cargar estructuras de datos que hagan el acoplado por usted. Esto permite indicar claramente qué se debe aplanar y qué no, y le brinda un control total sobre si este aplanamiento se realiza durante la carga o durante el acceso. Cuál elegir es una cuestión de facilidad de implementación y eficiencia en tiempo y espacio de almacenamiento. Esta es la misma disyuntiva que hay que hacer para implementar la combinación de la tecla función y no hay una solución única que siempre es la mejor.
Por ejemplo, mi ruamel.yaml
biblioteca usa los dictados combinados de fuerza bruta durante la carga cuando usa su cargador seguro, lo que da como resultado diccionarios combinados que son dictados normales de Python. Esta fusión debe realizarse por adelantado y duplica los datos (espacio ineficaz) pero es rápida en la búsqueda de valor. Al usar el cargador de viaje de ida y vuelta, desea poder volcar las fusiones sin fusionar, por lo que deben mantenerse separadas. El dict como estructura de datos cargada como resultado de la carga de ida y vuelta, es eficiente en el espacio pero más lento en el acceso, ya que necesita intentar buscar una clave que no se encuentra en el dict mismo en las fusiones (y esto no se almacena en caché, por lo que debe hacerse cada vez). Por supuesto, estas consideraciones no son muy importantes para archivos de configuración relativamente pequeños.
Lo siguiente implementa un esquema similar a la fusión para listas en Python usando objetos con etiqueta flatten
que sobre la marcha recurre a elementos que son listas y etiquetados toflatten
. Usando estas dos etiquetas puede tener un archivo YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(el uso de secuencias de estilo de flujo frente a bloque es completamente arbitrario y no influye en el resultado cargado).
Al iterar sobre los elementos que son el valor de la clave, m1
esto "recurre" a las secuencias etiquetadas contoflatten
, pero muestra otras listas (con alias o no) como un solo elemento.
Una forma posible con el código Python de lograrlo es:
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)
que salidas:
1
2
[3, 4]
[5, 6]
7
8
Como puede ver, puede ver, en la secuencia que necesita acoplarse, puede usar un alias para una secuencia etiquetada o puede usar una secuencia etiquetada. YAML no te permite hacer:
- !flatten *x2
, es decir, etiquetar una secuencia anclada, ya que esto esencialmente la convertiría en una estructura de datos diferente.
Usar etiquetas explícitas es IMO mejor que tener algo de magia como con las claves de combinación YAML <<
. Si nada más, ahora tiene que pasar por aros si tiene un archivo YAML con una asignación que tiene una clave
<<
que no desea que actúe como una clave de combinación, por ejemplo, cuando hace una asignación de operadores C a sus descripciones. en inglés (o en algún otro idioma natural).