Isto não vai funcionar:
merge só é compatível com as especificações YAML para mapeamentos e não para sequências
você está misturando completamente as coisas tendo uma chave de mesclagem <<
seguida pelo separador de chave / valor :
e um valor que é uma referência e então continua com uma lista no mesmo nível de indentação
YAML incorreto:
combine_stuff:
x: 1
- a
- b
Portanto, sua sintaxe de exemplo nem mesmo faria sentido como uma proposta de extensão YAML.
Se você deseja fazer algo como mesclar várias matrizes, pode considerar uma sintaxe como:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
onde s1
, s2
, s3
são âncoras sobre sequências (não mostrados) que você deseja mesclar em uma nova sequência e, em seguida, têm a d
, e
e f
em anexo a esse. Mas YAML está resolvendo esse tipo de profundidade de estruturas primeiro, portanto, não há contexto real disponível durante o processamento da chave de mesclagem. Não há matriz / lista disponível para você onde possa anexar o valor processado (a sequência ancorada).
Você pode seguir a abordagem proposta por @dreftymac, mas isso tem a grande desvantagem de que, de alguma forma, você precisa saber quais sequências aninhadas achatar (ou seja, conhecendo o "caminho" da raiz da estrutura de dados carregada até a sequência pai), ou que você percorre recursivamente a estrutura de dados carregada procurando por arrays / listas aninhados e nivela indiscriminadamente todos eles.
A melhor solução IMO seria usar tags para carregar estruturas de dados que fazem o achatamento para você. Isso permite denotar claramente o que precisa ser nivelado e o que não deve ser e oferece controle total sobre se esse nivelamento é feito durante o carregamento ou durante o acesso. Qual escolher é uma questão de facilidade de implementação e eficiência de tempo e espaço de armazenamento. Essa é a mesma compensação que precisa ser feita para implementar o recurso chave de mesclagem e não há uma solução única que seja sempre a melhor.
Por exemplo, minha ruamel.yaml
biblioteca usa merge-dicts de força bruta durante o carregamento ao usar seu carregador seguro, o que resulta em dicionários combinados que são dicts normais do Python. Essa mesclagem deve ser feita antecipadamente e duplica os dados (espaço ineficiente), mas é rápida na pesquisa de valor. Ao usar o carregador de ida e volta, você deseja poder despejar as mesclagens não mescladas, portanto, elas precisam ser mantidas separadas. O dicionário, como a estrutura de dados carregado como resultado do carregamento de ida e volta, é eficiente em termos de espaço, mas mais lento no acesso, pois precisa tentar e procurar uma chave não encontrada no próprio dicionário nas mesclagens (e isso não é armazenado em cache, então precisa ser feito o tempo todo). É claro que essas considerações não são muito importantes para arquivos de configuração relativamente pequenos.
O seguinte implementa um esquema de mesclagem para listas em python usando objetos com tag flatten
que recorre em tempo real em itens que são listas e marcados toflatten
. Usando essas duas tags, você pode ter um arquivo YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(o uso de sequências de estilo de fluxo vs bloco é completamente arbitrário e não tem influência no resultado carregado).
Ao iterar os itens que são o valor da chave, m1
isso "recorre" às sequências marcadas com toflatten
, mas exibe outras listas (com alias ou não) como um único item.
Uma maneira possível com o código Python de conseguir isso é:
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 produz:
1
2
[3, 4]
[5, 6]
7
8
Como você pode ver, na sequência que precisa ser nivelada, você pode usar um alias para uma sequência marcada ou uma sequência marcada. YAML não permite que você faça:
- !flatten *x2
, ou seja, marcar uma sequência ancorada, pois isso essencialmente a transformaria em uma estrutura de dados diferente.
Usar tags explícitas é IMO melhor do que ter alguma mágica acontecendo com as teclas de mesclagem YAML <<
. Se nada mais, você agora tem que passar por muitos obstáculos se acontecer de você ter um arquivo YAML com um mapeamento que tem uma chave
<<
que você não deseja que atue como uma chave de mesclagem, por exemplo, quando você faz um mapeamento de operadores C para suas descrições em inglês (ou alguma outra língua natural).