Mise à jour: Dans python 3.6+, vous n'avez probablement pas besoin OrderedDict
du tout en raison de la nouvelle implémentation de dict qui est utilisée dans pypy depuis un certain temps (bien que considérée comme un détail de l'implémentation CPython pour l'instant).
Mise à jour: dans python 3.7+, la nature de préservation de l'ordre d'insertion des objets dict a été déclarée comme faisant partie officielle de la spécification du langage Python , voir Nouveautés de Python 3.7 .
J'aime la solution de @James pour sa simplicité. Cependant, cela change la yaml.Loader
classe globale par défaut , ce qui peut entraîner des effets secondaires gênants. Surtout, lors de l'écriture de code de bibliothèque, c'est une mauvaise idée. De plus, cela ne fonctionne pas directement avec yaml.safe_load()
.
Heureusement, la solution peut être améliorée sans trop d'efforts:
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)
Pour la sérialisation, je ne connais pas de généralisation évidente, mais au moins cela ne devrait pas avoir d'effets secondaires:
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)
Le module yaml vous permet de spécifier des «représentants» personnalisés pour convertir des objets Python en texte et des «constructeurs» pour inverser le processus.
la source
from six import iteritems
, puis changez-le pouriteritems(data)
qu'il fonctionne aussi bien en Python 2 et 3.represent_dict
etDEFAULT_MAPPING_TAG
). Est-ce parce que la documentation est incomplète ou est-ce que ces fonctionnalités ne sont pas prises en charge et sont susceptibles d'être modifiées sans préavis?dict_constructor
vous devrez appelerloader.flatten_mapping(node)
ou vous ne pourrez pas charger<<: *...
(syntaxe de fusion)Option 2018:
oyaml
est un remplacement de PyYAML qui préserve l'ordre des dict. Python 2 et Python 3 sont pris en charge. Justepip install oyaml
et importez comme indiqué ci-dessous:Vous ne serez plus ennuyé par les mappages foirés lors du déchargement / chargement.
Remarque: je suis l'auteur d'oyaml.
la source
Option 2015 (et ultérieure):
ruamel.yaml remplace PyYAML (avertissement: je suis l'auteur de ce package). Préserver l'ordre des mappages était l'une des choses ajoutées dans la première version (0.1) en 2015. Non seulement cela préserve l'ordre de vos dictionnaires, mais il conservera également les commentaires, les noms d'ancrage, les balises et prend en charge le YAML 1.2 spécification (publiée en 2009)
La spécification dit que l'ordre n'est pas garanti, mais bien sûr, il y a un ordre dans le fichier YAML et l'analyseur approprié peut simplement s'en tenir à cela et générer de manière transparente un objet qui conserve l'ordre. Il vous suffit de choisir le bon analyseur, chargeur et dumper¹:
te donnera:
data
est du typeCommentedMap
qui fonctionne comme un dict, mais a des informations supplémentaires qui sont conservées jusqu'à ce qu'elles soient vidées (y compris le commentaire conservé!)la source
CommentedMap
directement mais ça ne marche pas, etOrderedDict
met!!omap
partout ce qui n'est pas très convivial.CommentedMap
avecsafe=True
inYAML
, qui n'a pas fonctionné (en utilisant dessafe=False
œuvres). J'ai également eu un problème deCommentedMap
ne pas être modifiable, mais je ne peux pas le reproduire maintenant ... J'ouvrirai une nouvelle question si je rencontre à nouveau ce problème.yaml = YAML()
, vous obtenez l'analyseur / dumper aller-retour et qui est dérivé de l'analyseur / dumper sûr qui connaît CommentedMap / Seq etc.Remarque : il existe une bibliothèque, basée sur la réponse suivante, qui implémente également le CLoader et les CDumpers: Phynix / yamlloader
Je doute fort que ce soit la meilleure façon de le faire, mais c'est ainsi que j'ai proposé et cela fonctionne. Également disponible sous forme de résumé .
la source
key_node.start_mark
attribut dans votre message d'erreur, je ne vois aucun moyen évident de simplifier votre boucle de construction centrale. Si vous essayez d'utiliser le fait que leOrderedDict
constructeur acceptera un itérable de paires clé / valeur, vous perdez l'accès à ce détail lors de la génération du message d'erreur.add_constructor
dans votre__init__
méthode.Mise à jour : la bibliothèque est obsolète au profit du yamlloader (qui est basé sur le yamlordereddictloader)
Je viens de trouver une bibliothèque Python ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ) qui a été créée à partir des réponses à cette question et est assez simple à utiliser:
la source
yodl
sur github.Sur mon installation For PyYaml pour Python 2.7, j'ai mis à jour __init__.py, constructor.py et loader.py. Prend désormais en charge l'option object_pairs_hook pour les commandes de chargement. La différence des modifications que j'ai apportées est ci-dessous.
la source
voici une solution simple qui vérifie également les clés de niveau supérieur dupliquées dans votre carte.
la source