Comment conserver les clés / valeurs dans le même ordre que celui déclaré?


320

J'ai un dictionnaire que j'ai déclaré dans un ordre particulier et je veux le garder dans cet ordre tout le temps. Les clés / valeurs ne peuvent pas vraiment être conservées dans l'ordre en fonction de leur valeur, je le veux juste dans l'ordre dans lequel je l'ai déclaré.

Donc, si j'ai le dictionnaire:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Ce n'est pas dans cet ordre si je le vois ou que je le parcoure, existe-t-il un moyen de s'assurer que Python gardera l'ordre explicite dans lequel j'ai déclaré les clés / valeurs?

Réponses:


229

A partir de Python 3.6, le dicttype standard conserve par défaut l'ordre d'insertion.

Définition

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

se traduira par un dictionnaire avec les clés dans l'ordre indiqué dans le code source.

Cela a été réalisé en utilisant un tableau simple avec des entiers pour la table de hachage éparse, où ces entiers indexent dans un autre tableau qui stocke les paires clé-valeur (plus le hachage calculé). Ce dernier tableau arrive juste pour stocker les éléments dans l'ordre d'insertion, et la combinaison entière utilise en fait moins de mémoire que l'implémentation utilisée dans Python 3.5 et avant. Voir le message d'idée original par Raymond Hettinger pour plus de détails.

En 3.6, cela était encore considéré comme un détail de mise en œuvre; voir le Nouveau en Python 3.6 de Qu'est - ce que la documentation :

L'aspect préservant l'ordre de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être invoqué (cela peut changer à l'avenir, mais il est souhaitable d'avoir cette nouvelle implémentation dict dans la langue pour quelques versions avant de changer la spécification de langue pour rendre obligatoire la sémantique préservant l'ordre pour toutes les implémentations Python actuelles et futures; cela permet également de préserver la compatibilité descendante avec les anciennes versions du langage où l'ordre d'itération aléatoire est toujours en vigueur, par exemple Python 3.5).

Python 3.7 élève ces détails d'implémentation à une spécification de langage , il est donc désormais obligatoire de dictconserver l'ordre dans toutes les implémentations Python compatibles avec cette version ou une version plus récente. Voir la déclaration du BDFL .

Vous pouvez toujours vouloir utiliser la collections.OrderedDict()classe dans certains cas, car elle offre des fonctionnalités supplémentaires en plus du dicttype standard . Tels que d'être réversible (cela s'étend aux objets de vue ) et de soutenir la réorganisation (via la move_to_end()méthode ).


Malheureusement, cela n'est pas pertinent pour la création d'un dictionnaire . Les dictionnaires spécifiés comme celui de l'exemple dans le code ne sont pas garantis pour maintenir le même ordre. Cela signifie que vous devez créer un dict vide et insérer manuellement chaque élément si vous souhaitez contrôler la commande.
naught101

8
@ naught101: non, cela ne s'applique à la création de dict. En Python 3.7 et supérieur, l'utilisation d'un affichage dict comme indiqué dans la question est garanti pour lister ces clés dans l'ordre . Les paires clé-valeur écrites sont insérées de gauche à droite, car la spécification de langue garantit que : Si une séquence de paires clé / donnée séparée par des virgules est donnée, elles sont évaluées de gauche à droite pour définir les entrées du dictionnaire . La dict()documentation inclut même un exemple.
Martijn Pieters

175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

contient

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Si les valeurs sont True(ou tout autre objet immuable), vous pouvez également utiliser:

OrderedDict.fromkeys(words, True)

2
Il convient de noter, bien sûr, que la partie «immuable» n'est pas une règle stricte et rapide que Python appliquera - ce n'est «qu'une» bonne idée.
lvc

11
sachez que des solutions telles que: OrderedDict(FUTURE=[], TODAY=[], PAST=[])ne fonctionneront pas, lorsqu'elles sont mentionnées: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])gardera l'ordre.
andilabs

2
@andi J'ai eu un autre problème, lors de l'utilisation de jsonify, le OrderedDict semble perdu son ordre lors de la génération des données json.
tyan

github.com/pallets/flask/issues/974 cela peut être utilisé pour résoudre le problème ..
tyan

5
Python3.7 a maintenant dict ordonné par défaut. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat

167

Plutôt que d'expliquer la partie théorique, je vais donner un exemple simple.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}

16
Existe-t-il un moyen d'affecter en masse OrderedDict comme le type Dict?
tyan

2
OrderedDictrésout en effet le problème, mais ... dans cet exemple particulier, vous obtenez exactement le même résultat en utilisant un dictionnaire standard
Tonechas

2
@Tonechas: Je viens d'essayer l'exemple avec un dictionnaire standard et j'ai {'aol': 1, 'foo': 3}donc obtenu que je pense que c'est un bon exemple illustratif.
twasbrillig

4
Il y a une leçon pour tout le monde: il a été découvert (je pense autour de la version 2.4) que le hachage prévisible de Python pourrait donner lieu à des vulnérabilités de sécurité , donc maintenant il n'y a aucune garantie que même deux exécutions différentes du même code donneront le même ordre dans un standard dict.
holdenweb

1
@tyan vous pouvez appeler OrderedDict.update()avec une paire itérables contenant valeur clé: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen

37

Notez que cette réponse s'applique aux versions de python antérieures à python3.7. CPython 3.6 conserve l'ordre d'insertion dans la plupart des circonstances comme détail d'implémentation. À partir de Python3.7, il a été déclaré que les implémentations DOIVENT maintenir l'ordre d'insertion pour être conformes.


les dictionnaires python ne sont pas classés. Si vous voulez un dictionnaire ordonné, essayez collections.OrderedDict .

Notez que OrderedDict a été introduit dans la bibliothèque standard en python 2.7. Si vous avez une ancienne version de python, vous pouvez trouver des recettes pour les dictionnaires commandés sur ActiveState .


voir le post de @ martijn ci-dessus. À partir de python 3.6, dict prend en charge l'ordre d'insertion.
tpk

13

Les dictionnaires utiliseront un ordre qui rend la recherche efficace, et vous ne pouvez pas changer cela,

Vous pouvez simplement utiliser une liste d'objets (un tuple à 2 éléments dans un cas simple, ou même une classe) et ajouter des éléments à la fin. Vous pouvez ensuite utiliser la recherche linéaire pour y rechercher des éléments.

Vous pouvez également créer ou utiliser une structure de données différente créée dans le but de maintenir l'ordre.


Les dictionnaires utiliseront un ordre qui rend la recherche efficace. Enfin, quelqu'un l'a signalé.
scharette

7

Je suis tombé sur ce post en essayant de comprendre comment faire fonctionner OrderedDict. PyDev pour Eclipse n'a pas pu trouver OrderedDict du tout, j'ai donc décidé de faire un tuple des valeurs clés de mon dictionnaire comme je voudrais qu'elles soient commandées. Lorsque j'ai eu besoin de sortir ma liste, je viens de parcourir les valeurs du tuple et de brancher la 'clé' itérée du tuple dans le dictionnaire pour récupérer mes valeurs dans l'ordre dont j'avais besoin.

exemple:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

C'est un peu lourd, mais je suis pressé par le temps et c'est la solution de contournement que j'ai trouvée.

Remarque: l'approche de la liste de listes que quelqu'un a suggérée n'a pas vraiment de sens pour moi, car les listes sont ordonnées et indexées (et sont également une structure différente de celle des dictionnaires).


Excellente solution. Je vais l'utiliser pour écrire json dans un fichier, toujours dans le même ordre.
Hrvoje T

6

Vous ne pouvez pas vraiment faire ce que vous voulez avec un dictionnaire. Vous avez déjà d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}créé le dictionnaire . J'ai trouvé qu'il n'y avait aucun moyen de maintenir l'ordre une fois qu'il était déjà créé. J'ai plutôt créé un fichier json avec l'objet:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

J'ai utilisé:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

puis utilisé:

print json.dumps(r)

vérifier.


1
Alors pourquoi ne pas commencer par un OrderedDict à partir d'une liste? Le fichier JSON n'ajoute vraiment rien ici.
Martijn Pieters

Oui, la liste est plus utile pour maintenir l'ordre, mais la réponse concernait la question de la commande des dictionnaires. Faire simplement savoir aux gens les limites de l'utilisation d'un dictionnaire et leur donner une solution possible s'ils ont besoin d'utiliser un dictionnaire pour une raison quelconque.
nealous3

1
Mais cette partie est déjà couverte par des réponses beaucoup plus anciennes, remontant à 2012.
Martijn Pieters

3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])

problème principal qui n'est plus un dicton, c'est une liste de tuples
Oleg

2

Une autre alternative consiste à utiliser Pandas dataframecar il garantit l'ordre et les emplacements d'index des articles dans une structure de type dict.


1

En général, vous pouvez concevoir une classe qui se comporte comme un dictionnaire, principalement la mise en œuvre des méthodes __contains__, __getitem__, __delitem__, __setitem__et un peu plus. Cette classe peut avoir n'importe quel comportement que vous aimez, par exemple en prévoyant un itérateur trié sur les clés ...


1

si vous souhaitez avoir un dictionnaire dans un ordre spécifique, vous pouvez également créer une liste de listes, où le premier élément sera la clé, et le deuxième élément sera la valeur et ressemblera à cet exemple

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3

7
Ce n'est pas un "dictionnaire" car vous ne pouvez pas rechercher des éléments par leur clé sans rechercher dans toute la collection (en prenant le temps O (n)).
BHSPitMonkey

1
Oui, ce n'est pas un dictionnaire, mais, selon la situation, il pourrait apporter une solution valable au problème de l'affiche originale.
SunSparc

il n'a pas dit exactement comment nous voulions, juste que je veux pouvoir les commander =), comme toujours il y a beaucoup de façons de faire une chose.
pelos

1

J'ai eu un problème similaire lors du développement d'un projet Django. Je ne pouvais pas utiliser OrderedDict, car j'utilisais une ancienne version de python, donc la solution était d'utiliser la classe SortedDict de Django:

https://code.djangoproject.com/wiki/SortedDict

par exemple,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Remarque: Cette réponse date de 2011. Si vous avez accès à Python version 2.7 ou supérieure, vous devriez avoir accès à la norme désormais collections.OrderedDict, dont de nombreux exemples ont été fournis par d'autres dans ce fil.


0

Vous pouvez faire la même chose que pour le dictionnaire.

Créez une liste et videz le dictionnaire:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.