Gostaria que o carregador do PyYAML carregasse mapeamentos (e ordenados) no tipo OrderedDict do Python 2.7+ , em vez da baunilha dict
e da lista de pares que ela usa atualmente.
Qual é a melhor maneira de fazer isso?
fonte
Gostaria que o carregador do PyYAML carregasse mapeamentos (e ordenados) no tipo OrderedDict do Python 2.7+ , em vez da baunilha dict
e da lista de pares que ela usa atualmente.
Qual é a melhor maneira de fazer isso?
Atualização: No python 3.6+, você provavelmente não precisa de OrderedDict
todo devido à nova implementação do dict que está em uso no pypy há algum tempo (embora considerado o detalhe da implementação do CPython por enquanto).
Atualização: No python 3.7+, a natureza de preservação da ordem de inserção dos objetos dict foi declarada parte integrante da especificação da linguagem Python , consulte O que há de novo no Python 3.7 .
Eu como @ James solução para a sua simplicidade. No entanto, ele altera a yaml.Loader
classe global padrão , o que pode levar a efeitos colaterais problemáticos. Especialmente, ao escrever o código da biblioteca, é uma má idéia. Além disso, ele não funciona diretamente yaml.safe_load()
.
Felizmente, a solução pode ser melhorada sem muito esforço:
import yaml
from collections import OrderedDict
def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
class OrderedLoader(Loader):
pass
def construct_mapping(loader, node):
loader.flatten_mapping(node)
return object_pairs_hook(loader.construct_pairs(node))
OrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_mapping)
return yaml.load(stream, OrderedLoader)
# usage example:
ordered_load(stream, yaml.SafeLoader)
Para serialização, não conheço uma generalização óbvia, mas pelo menos isso não deve ter efeitos colaterais:
def ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds):
class OrderedDumper(Dumper):
pass
def _dict_representer(dumper, data):
return dumper.represent_mapping(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
data.items())
OrderedDumper.add_representer(OrderedDict, _dict_representer)
return yaml.dump(data, stream, OrderedDumper, **kwds)
# usage:
ordered_dump(data, Dumper=yaml.SafeDumper)
O módulo yaml permite especificar 'representantes' personalizados para converter objetos Python em texto e 'construtores' para reverter o processo.
fonte
from six import iteritems
e, em seguida, alterá-lo paraiteritems(data)
assim que ele funciona igualmente bem em Python 2 e 3.represent_dict
eDEFAULT_MAPPING_TAG
). Isso ocorre porque a documentação está incompleta ou esses recursos não são suportados e estão sujeitos a alterações sem aviso prévio?dict_constructor
você vai precisar ligarloader.flatten_mapping(node)
ou você não será capaz de carregar<<: *...
(sintaxe merge)2018 opção:
oyaml
é um substituto para o PyYAML, que preserva a ordem dos ditados . O Python 2 e o Python 3 são suportados. Justpip install oyaml
e importe conforme mostrado abaixo:Você não será mais incomodado por mapeamentos errados ao descarregar / carregar.
Nota: Eu sou o autor de oyaml.
fonte
Opção 2015 (e posterior):
ruamel.yaml é uma queda no substituto do PyYAML (exoneração de responsabilidade: eu sou o autor desse pacote). Preservar a ordem dos mapeamentos foi uma das coisas adicionadas na primeira versão (0.1) em 2015. Não apenas preserva a ordem dos seus dicionários, mas também preserva comentários, nomes de âncoras, tags e suporta o YAML 1.2 especificação (lançado em 2009)
A especificação diz que a ordem não é garantida, mas é claro que há pedidos no arquivo YAML e o analisador apropriado pode apenas se apegar a isso e gerar transparentemente um objeto que os mantenha. Você só precisa escolher o analisador, carregador e dumper certos¹:
Darei à você:
data
é do tipoCommentedMap
que funciona como um ditado, mas tem informações extras que são mantidas por aí até serem descartadas (incluindo o comentário preservado!)fonte
CommentedMap
diretamente, mas ele não funciona, eOrderedDict
coloca em!!omap
todos os lugares que não é muito fácil de usar.CommentedMap
withsafe=True
inYAML
, que não funcionou (usando osafe=False
works). Também tive problemas emCommentedMap
não ser modificável, mas não posso reproduzi-lo agora ... Vou abrir uma nova pergunta se encontrar esse problema novamente.yaml = YAML()
, você obtém o analisador / descarregador de ida e volta e isso é derivado do analisador / descarregador seguro que sabe sobre o CommentedMap / Seq etc.Nota : existe uma biblioteca, com base na seguinte resposta, que implementa também o CLoader e CDumpers: Phynix / yamlloader
Eu duvido muito que essa seja a melhor maneira de fazê-lo, mas essa é a maneira que eu criei e funciona. Também disponível como uma essência .
fonte
key_node.start_mark
atributo na sua mensagem de erro, não vejo nenhuma maneira óbvia de simplificar seu loop de construção central. Se você tentar fazer uso do fato de que oOrderedDict
construtor aceitará uma iterável de pares de chave e valor, você perderá o acesso a esses detalhes ao gerar a mensagem de erro.add_constructor
no seu__init__
método.Atualização : a biblioteca foi preterida em favor do yamlloader (que é baseado no yamlordereddictloader)
Acabei de encontrar uma biblioteca Python ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ) que foi criada com base nas respostas a esta pergunta e é bastante simples de usar:
fonte
yodl
no github.Na instalação do For PyYaml para Python 2.7, atualizei __init__.py, constructor.py e loader.py. Agora suporta a opção object_pairs_hook para comandos de carregamento. A diferença das alterações que fiz está abaixo.
fonte
aqui está uma solução simples que também verifica chaves duplicadas de nível superior em seu mapa.
fonte