web-dev-qa-db-fra.com

Les dictionnaires sont-ils commandés dans Python 3.6+?

Les dictionnaires sont classés dans Python 3.6 (du moins dans l’implémentation de CPython) contrairement aux incarnations précédentes. Cela semble être un changement substantiel, mais ce n’est qu’un court paragraphe de la documentation . Il est décrit comme un détail d'implémentation CPython plutôt que comme une fonctionnalité de langage, mais implique également que cela deviendra la norme à l'avenir.

Comment la nouvelle implémentation du dictionnaire fonctionne-t-elle mieux que l'ancienne, tout en préservant l'ordre des éléments?

Voici le texte de la documentation:

dict() utilise maintenant une représentation “compacte” inventée par PyPy . L'utilisation de la mémoire du nouveau dict () est réduite de 20% à 25% par rapport à Python 3.5. PEP 468 (Préserver l'ordre de ** kwargs dans une fonction.) Est implémenté par ceci. 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 dans le futur, mais nous souhaitons avoir cette nouvelle implémentation dict dans le langage pour quelques versions avant de changer les spécifications de langage. pour imposer une sémantique préservant l'ordre dans toutes les implémentations actuelles et futures de Python; cela aide également à préserver la compatibilité avec les versions antérieures du langage où l'ordre d'itération aléatoire est toujours actif, par exemple Python 3,5). (Contribution de INADA Naoki dans numéro 2735 . Idée initialement proposée par Raymond Hettinger .)

Mise à jour de décembre 2017: dicts conservant l'ordre d'insertion est garanti pour Python 3.7

355
Chris_Rands

Les dictionnaires sont-ils ordonnés dans Python 3.6 +?

Ils sont insertion ordonnée [1] . À partir de Python 3.6, pour l’implémentation CPython de Python, les dictionnaires mémorise 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 que l'ordre d'insertion soit garanti sur d'autres implémentations de Python (et d'autres comportements ordonnés [1] ).

À partir de Python 3.7 , il ne s'agit plus d'un détail d'implémentation, mais d'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 . Les autres implémentations de Python doivent également proposer un dictionnaire avec ordre d'insertion si elles souhaitent être une implémentation conforme de Python 3.7.


Comment la mise en œuvre du dictionnaire Python _3.6_ fonctionne-t-elle mieux?[2] que l'ancien en conservant l'ordre des éléments?

Essentiellement, par en gardant deux tableaux .

  • Le premier tableau, dk_entries , contient les entrées ( de type PyDictKeyEntry ) pour le dictionnaire dans l'ordre dans lequel elles ont été insérées. La préservation de l’ordre est obtenue par le fait qu’il s’agit d’un tableau d’ajout uniquement, dans lequel les nouveaux éléments sont toujours insérés à la fin (ordre d'insertion).

  • Le second, dk_indices , contient les index du tableau _dk_entries_ (c'est-à-dire les valeurs indiquant la position de l'entrée correspondante dans _dk_entries_). Ce tableau agit comme une 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 l'indexation _dk_entries_. Comme 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 _32_/_64_ générations de bits)

Dans l'implémentation précédente, un tableau fragmenté 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 de _2/3 * dk_size_ full pour des raisons de performances . (et l’espace vide toujours avait PyDictKeyEntry taille!).

Ce n'est pas le cas maintenant car seules les entrées obligatoire sont stockées (celles qui ont été insérées) et un tableau fragmenté de type _intX_t_ (X selon sur la taille dict) _2/3 * dk_size_ s complet est conservé. L'espace vide est passé de type PyDictKeyEntry à _intX_t_.

Donc, bien évidemment, créer un tableau fragmenté de type PyDictKeyEntry nécessite beaucoup plus de mémoire qu'un tableau fragmenté pour stocker ints.

Vous pouvez voir la conversation complète sur Python-Dev à propos de cette fonctionnalité si cela vous intéresse, 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 capture l’essentiel de l’idée.

Par exemple, le dictionnaire:

_d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
_

est actuellement stocké sous:

_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 maintenant le constater visuellement, dans la proposition initiale, beaucoup d'espace est essentiellement vide pour réduire les collisions et permettre des recherches plus rapides. Avec la nouvelle approche, vous réduisez la mémoire requise en déplaçant la parcimonie là où elle est réellement requise, dans les index.


[1]: Je dis "insertion ordonnée" et non pas "ordonnée" car, avec l'existence de OrderedDict, "ordonné" suggère un comportement supplémentaire que l'objet dict de l'objet ne fournit pas . OrderedDicts est réversible, fournit des méthodes sensibles à l'ordre et, principalement, un test d'égalité sensible à l'ordre (_==_, _!=_). dicts n'offrent 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 ce qui concerne la vitesse, la différence n’est pas si radicale, il existe des endroits où le nouveau dict peut introduire de légères régressions ( recherches de touches, par exemple ), tandis que dans d’autres (on pense à des itérations et des redimensionnements) devrait être présent.

Globalement, les performances du dictionnaire, en particulier dans des situations réelles, s’améliorent grâce à la compacité introduite.

387

Ci-dessous, vous répondez à la première question initiale:

Devrais-je utiliser dict ou OrderedDict dans Python 3.6?

Je pense que cette phrase de la documentation est suffisante pour répondre à votre question

L'aspect préservant les commandes de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être invoqué.

dict n'est pas explicitement censé être une collection ordonnée. Par conséquent, si vous souhaitez rester cohérent et ne pas compter sur un effet secondaire de la nouvelle implémentation, vous devez vous en tenir à OrderedDict.

Faites de votre code une preuve future :)

Il y a un débat à ce sujet ici .

EDIT: Python 3.7 conservera cette fonctionnalité voir

62
Maresh

Mise à jour: Guido van Rossum annoncé sur la liste de diffusion qu'à partir de Python 3.7 dicts dans toutes les implémentations Python, vous devez conserver l'ordre d'insertion.

19
fjsj

Je voulais ajouter à la discussion ci-dessus mais je n'ai pas la réputation de commenter.

Python 3.8 n'est pas encore complètement publié, mais il inclura même la fonction reversed() sur les dictionnaires (ce qui supprime une autre différence avec OrderedDict.

Dict et dictviews sont maintenant éditables dans l'ordre d'insertion inversé à l'aide de reverse (). (Contribution de Rémi Lapeyre dans bpo-33462.) Voir les nouveautés dans python 3.8

Je ne vois aucune mention de l'opérateur d'égalité ou d'autres fonctionnalités de OrderedDict, de sorte qu'elles ne sont toujours pas tout à fait les mêmes.

1
rkengler