J'utilise yaml.dump
pour générer un dict. Il imprime chaque article dans l'ordre alphabétique en fonction de la clé.
>>> d = {"z":0,"y":0,"x":0}
>>> yaml.dump( d, default_flow_style=False )
'x: 0\ny: 0\nz: 0\n'
Existe-t-il un moyen de contrôler l’ordre des paires clé/valeur?
Dans mon cas d'utilisation particulier, l'impression inversée serait (par coïncidence) suffisante. Pour être complet, je cherche une réponse qui montre comment contrôler l’ordre plus précisément.
J'ai envisagé d'utiliser collections.OrderedDict
mais PyYAML ne semble pas le supporter. J'ai également examiné le sous-classement yaml.Dumper
, mais je n'ai pas été en mesure de déterminer s'il peut modifier l'ordre des articles.
Il existe probablement une meilleure solution de contournement, mais je n'ai rien trouvé dans la documentation ni dans la source.
Python 2 (voir commentaires)
J'ai sous-classé OrderedDict
et lui ai renvoyé une liste d'éléments non triables:
from collections import OrderedDict
class UnsortableList(list):
def sort(self, *args, **kwargs):
pass
class UnsortableOrderedDict(OrderedDict):
def items(self, *args, **kwargs):
return UnsortableList(OrderedDict.items(self, *args, **kwargs))
yaml.add_representer(UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict)
Et cela semble fonctionner:
>>> d = UnsortableOrderedDict([
... ('z', 0),
... ('y', 0),
... ('x', 0)
... ])
>>> yaml.dump(d, default_flow_style=False)
'z: 0\ny: 0\nx: 0\n'
Python 3 ou 2 (voir commentaires)
Vous pouvez également écrire un représentant personnalisé, mais je ne sais pas si vous rencontrerez des problèmes plus tard, car j'ai enlevé du code de vérification de style:
import yaml
from collections import OrderedDict
def represent_ordereddict(dumper, data):
value = []
for item_key, item_value in data.items():
node_key = dumper.represent_data(item_key)
node_value = dumper.represent_data(item_value)
value.append((node_key, node_value))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
yaml.add_representer(OrderedDict, represent_ordereddict)
Mais avec cela, vous pouvez utiliser la classe native OrderedDict
.
Si vous mettez à niveau PyYAML vers la version 5.1, il prend désormais en charge le dump sans trier les clés de la manière suivante:
yaml.dump(data, default_flow_style=False, sort_keys=False)
c’est très nouveau, il vient d’être corrigé il ya quelques heures lorsque je tape.
Un seul paquebot pour les gouverner tous:
yaml.add_representer(dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))
C'est tout. Finalement. Après toutes ces années et ces heures, le puissant represent_dict
a été mis en échec en lui donnant le dict.items()
au lieu de dict
Voici comment cela fonctionne:
Voici le code source PyYaml pertinent:
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
try:
mapping = sorted(mapping)
except TypeError:
pass
for item_key, item_value in mapping:
Pour éviter le tri, nous avons simplement besoin d'un objet Iterable[Pair]
qui n'a pas .items()
.
dict_items
est un candidat idéal pour cela.
Voici comment faire cela sans affecter l'état global du module yaml:
#Using a custom Dumper class to prevent changing the global state
class CustomDumper(yaml.Dumper):
#Super neat hack to preserve the mapping key order. See https://stackoverflow.com/a/52621703/1497385
def represent_dict_preserve_order(self, data):
return self.represent_dict(data.items())
CustomDumper.add_representer(dict, CustomDumper.represent_dict_preserve_order)
return yaml.dump(component_dict, Dumper=CustomDumper)
Ceci est vraiment juste un addendum à la réponse de @ Blender. Si vous regardez dans la source PyYAML
, dans le module representer.py
, vous trouvez cette méthode:
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = mapping.items()
mapping.sort()
for item_key, item_value in mapping:
node_key = self.represent_data(item_key)
node_value = self.represent_data(item_value)
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
Si vous supprimez simplement la ligne mapping.sort()
, elle conserve l'ordre des éléments dans la variable OrderedDict
.
Une autre solution est donnée dans ce post . Cela ressemble à @ Blender, mais fonctionne pour safe_dump
. L'élément commun est la conversion du dict en une liste de n-uplets. Le contrôle if hasattr(mapping, 'items')
est évalué à false.
Mettre à jour:
Je viens de remarquer que le référentiel EPEL du projet Fedora contient un package appelé python2-yamlordereddictloader
, et il en existe un pour Python 3 également. Le projet en amont pour ce package est probablement multiplate-forme.
Il y a deux choses que vous devez faire pour obtenir ceci comme vous voulez:
dict
, car elle ne garde pas les articles commandésimport sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap
d = CommentedMap()
d['z'] = 0
d['y'] = 0
d['x'] = 0
ruamel.yaml.round_trip_dump(d, sys.stdout)
sortie:
z: 0
y: 0
x: 0
¹ Cela a été fait en utilisant ruamel.yaml un analyseur syntaxique YAML 1.2, dont je suis l'auteur.
Pour Python 3.7+, les dictés préservent l'ordre d'insertion. Il est préférable d'utiliser une bibliothèque respectant cela, telle que oyaml :
>>> import oyaml as yaml # pip install oyaml
>>> d = {"z": 0, "y": 0, "x": 0}
>>> yaml.dump(d, default_flow_style=False)
'z: 0\ny: 0\nx: 0\n'
Si safe_dump
(c'est-à-dire dump
avec _Dumper=SafeDumper
_) est utilisé, l'appel de _yaml.add_representer
_ n'a aucun effet. Dans ce cas, il est nécessaire d'appeler explicitement la méthode _add_representer
_ sur SafeRepresenter
class:
_yaml.representer.SafeRepresenter.add_representer(
OrderedDict, ordered_dict_representer
)
_