Les dictionnaires sont-ils commandés dans Python 3.6+?
Ils sont ordonnés par insertion [1] . Depuis Python 3.6, pour l'implémentation CPython de Python, les dictionnaires se souviennent de l'ordre des éléments insérés . Ceci est considéré comme un détail d'implémentation dans Python 3.6 ; vous devez utiliser OrderedDict
si vous souhaitez un ordre d'insertion garanti dans d'autres implémentations de Python (et d'autres comportements ordonnés [1] ).
À partir de Python 3.7 , ce n'est plus un détail d'implémentation et devient plutôt une fonctionnalité de langage. À partir d'un message python-dev de GvR :
Faire en sorte. "Dict conserve l'ordre d'insertion" est la décision. Merci!
Cela signifie simplement que vous pouvez en dépendre . D'autres implémentations de Python doivent également offrir un dictionnaire d'insertion ordonné si elles souhaitent être une implémentation conforme de Python 3.7.
Comment l' 3.6
implémentation du dictionnaire Python fonctionne-t-elle mieux [2] que l'ancienne tout en préservant l'ordre des éléments?
Essentiellement, en conservant deux tableaux .
Le premier tableau,, dk_entries
contient les entrées ( de typePyDictKeyEntry
) pour le dictionnaire dans l'ordre où elles ont été insérées. La préservation de l'ordre est obtenue en étant un tableau d'ajout uniquement où de nouveaux éléments sont toujours insérés à la fin (ordre d'insertion).
Le second, dk_indices
contient les indices du dk_entries
tableau (c'est-à-dire les valeurs qui indiquent la position de l'entrée correspondante dans dk_entries
). Ce tableau fait office de table de hachage. Lorsqu'une clé est hachée, elle conduit à l'un des index stockés dans dk_indices
et l'entrée correspondante est extraite par indexation dk_entries
. Étant donné que seuls les index sont conservés, le type de ce tableau dépend de la taille globale du dictionnaire (allant du type int8_t
( 1
octet) à int32_t
/ int64_t
( 4
/ 8
octets) sur les versions 32
/ 64
bit)
Dans l'implémentation précédente, un tableau clairsemé de type PyDictKeyEntry
et de taille dk_size
devait être alloué; malheureusement, cela a également entraîné beaucoup d'espace vide car ce tableau n'était pas autorisé à être plus que 2/3 * dk_size
plein pour des raisons de performances . (et l'espace vide avait encore de laPyDictKeyEntry
taille!).
Ce n'est pas le cas maintenant car seules les entrées requises sont stockées (celles qui ont été insérées) et un tableau clairsemé de type intX_t
( X
selon la taille du dict) 2/3 * dk_size
est plein. L'espace vide est passé de type PyDictKeyEntry
à intX_t
.
Donc, évidemment, la création d'un tableau de type PyDictKeyEntry
clairsemé demande beaucoup plus de mémoire qu'un tableau clairsemé pour stocker int
s.
Vous pouvez voir la conversation complète sur Python-Dev concernant cette fonctionnalité si vous êtes intéressé, c'est une bonne lecture.
Dans la proposition originale faite par Raymond Hettinger , on peut voir une visualisation des structures de données utilisées qui saisit l'essentiel de l'idée.
Par exemple, le dictionnaire:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
est actuellement stocké sous [keyhash, key, value]:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
Au lieu de cela, les données doivent être organisées comme suit:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
Comme vous pouvez le voir visuellement maintenant, dans la proposition d'origine, beaucoup d'espace est essentiellement vide pour réduire les collisions et accélérer les recherches. Avec la nouvelle approche, vous réduisez la mémoire requise en déplaçant la rareté là où elle est vraiment nécessaire, dans les index.
[1]: Je dis "insertion ordonnée" et non "ordonnée" car, avec l'existence de OrderedDict, "ordonné" suggère un comportement supplémentaire que l' dict
objet ne fournit pas . OrderedDicts sont réversibles, fournissent des méthodes sensibles à l'ordre et, principalement, fournissent des tests d'égalité sensibles à l'ordre ( ==
, !=
). dict
s ne proposent actuellement aucun de ces comportements / méthodes.
[2]: Les nouvelles implémentations de dictionnaire fonctionnent mieux en termes de mémoire en étant conçues de manière plus compacte; c'est le principal avantage ici. En termes de vitesse, la différence n'est pas si drastique, il y a des endroits où le nouveau dict peut introduire de légères régressions ( recherches de touches, par exemple ) tandis que dans d'autres (itération et redimensionnement viennent à l'esprit) un boost de performance devrait être présent.
Dans l'ensemble, les performances du dictionnaire, en particulier dans des situations réelles, s'améliorent en raison de la compacité introduite.