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 OrderedDictsi 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.6implé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_entriescontient 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_indicescontient les indices du dk_entriestableau (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_indiceset 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( 1octet) à int32_t/ int64_t( 4/ 8octets) sur les versions 32/ 64bit)
Dans l'implémentation précédente, un tableau clairsemé de type PyDictKeyEntryet de taille dk_sizedevait ê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_sizeplein 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( Xselon la taille du dict) 2/3 * dk_sizeest plein. L'espace vide est passé de type PyDictKeyEntryà intX_t.
Donc, évidemment, la création d'un tableau de type PyDictKeyEntryclairsemé demande beaucoup plus de mémoire qu'un tableau clairsemé pour stocker ints.
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' dictobjet ne fournit pas . OrderedDicts sont réversibles, fournissent des méthodes sensibles à l'ordre et, principalement, fournissent des tests d'égalité sensibles à l'ordre ( ==, !=). dicts 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.