Pourquoi les dictionnaires python ne sont-ils pas réversibles pour python3.7?

11

À partir de la version 3.7, les dictionnaires python standard sont garantis pour maintenir l'ordre d'insertion. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

En d'autres termes, les touches dict sont conservées dans un ordre strict. En principe, cela permettrait aux clés d'être réversibles. Cependant, aucun des travaux suivants:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Questions: Quel est le raisonnement derrière ce comportement? Pourquoi les pronostics n'ont-ils pas été rendus réversibles? Est-il prévu de changer ce comportement à l'avenir?

La solution de contournement fonctionne bien sûr:

for k in reversed(list(d.keys())): 
    print(k)

(*) En fait, c'est déjà le cas pour les installations typiques de python 3.6, comme discuté dans cet article .


Mise à jour : à partir de python 3.8, les dictés sont réellement réversibles. La réponse acceptée fait référence à la discussion entre Guido et d'autres développeurs principaux qui a conduit à cette décision. En un mot, ils ont pondéré la cohérence du langage par rapport aux efforts de mise en œuvre et aux avantages réels pour les utilisateurs.

normanius
la source

Réponses:

5

De la documentation :

inversé ( seq )

Retournez un revers iterator. seq doit être un objet qui a une __reversed__()méthode ou prend en charge le protocole de séquence (la __len__()méthode et la __getitem__()méthode avec des arguments entiers commençant à 0).

Un dictobjet n'est pas implémenté __reversed__. Il met en œuvre les deux dernières méthodes. Cependant, __getitem__prend des clés comme arguments plutôt que des entiers (à partir de 0).

Quant à savoir pourquoi, cela a déjà été suggéré et discuté ici .

ÉDITER:

Ces citations proviennent de la liste de diffusion Python-Dev (thread "Add __reversed__ methods for dict", démarré le 25. 05. 18), je vais commencer par les arguments "conceptuels", le premier vient d'Antoine Pitrou:

Cela ne vaut rien que OrderedDict supporte déjà inversé (). L'argument pourrait aller dans les deux sens:

  1. dict est similaire à OrderedDict de nos jours, il devrait donc également prendre en charge reverse ();

  2. vous pouvez utiliser OrderedDict pour signaler explicitement que vous vous souciez de la commande; pas besoin d'ajouter quoi que ce soit à dicter.

Je pense que l'ordre d'insertion garanti pour les textes normaux est tout nouveau, il faudra donc un certain temps pour que la notion s'installe et fasse partie de la réflexion quotidienne sur les textes. Une fois que cela se produit, il est probablement inévitable que des cas d'utilisation émergent et que __reversed__ soit ajouté à un moment donné. La mise en œuvre semble simple et il ne s'agit pas d'un grand saut conceptuel de s'attendre à ce qu'une collection ordonnée finie soit réversible.

Suivi de la réponse de Raymond Hettinger:

Étant donné que les dicts suivent désormais l'ordre d'insertion, il semble raisonnable de vouloir connaître les insertions les plus récentes (c'est-à-dire boucler sur les tâches les plus récemment ajoutées dans un dict de tâche). D'autres cas d'utilisation possibles correspondront probablement à la façon dont nous utilisons la commande queue Unix.

Si ces cas d'utilisation se présentent, il serait bien que __reversed__ soit déjà pris en charge afin que les gens ne soient pas tentés d'implémenter une solution de contournement laide en utilisant des appels popitem () suivis de réinsertions.

La principale préoccupation exprimée dans la liste de diffusion était que cela ajouterait trop de ballonnement ou réduirait l'efficacité de la mémoire (avoir des listes doublement liées au lieu de celles liées individuellement) dans au moins certaines implémentations, voici la citation d'Inada Naoki de Python bug tracker ( problème 33462 ):

"Avoir un ordre" ne signifie pas "réversible". Par exemple, une seule liste chaînée est ordonnée, mais non réversible.

Alors que l'implémentation de CPython peut être efficace __reverse__, l'ajout __reverse__signifie que toute implémentation de Python devrait le fournir. Par exemple, certaines implémentations Python peuvent être capables d'implémenter dict avec hashmap + liste liée unique. Si __reverse__est ajouté, ce n'est plus possible.

De retour à la liste de diffusion, voici les deux derniers messages (tous deux postés le 08.06.2018). Le premier est de Michael Selik:

Ai-je raison de dire que le consensus est +1 pour l'inclusion dans la v3.8?

Le dernier point de la discussion était INADA Naoki recherchant diverses implémentations et décidant qu'il est OK d'inclure cette fonctionnalité dans 3.8. Si je comprends bien, Guido était d'accord avec les conseils de l'INADA d'attendre la mise en œuvre de MicroPython de la v3.7. Depuis INADA a changé d'avis, je suppose que tout est en faveur?

En conclusion avec le message de Guido van Rossum:

Cela me semble juste. Nous aurons alors eu deux versions où ce fut le cas:

  • 3.6 où la préservation de l'ordre a été implémentée dans CPython mais dans la spécification de langue

  • 3.7 où il a également été ajouté à la spécification de langue

Comme indiqué dans les autres réponses et commentaires, reversed()est pris en charge pour les dict et dictviews depuis la version 3.8 (14.10.2018).

gst
la source
5
Votre deuxième citation semble être une sélection biaisée de ce fil. Le consensus semble être que la fonctionnalité sera ajoutée en 3.8. Avant Python 3.7, il n'y avait tout simplement pas de commande sur un dictobjet normal (du moins non garanti par le langage), donc cela reversedn'avait aucun sens
FlyingTeller
Je n'ai pas de chien dans le combat, je viens de citer sa première réponse. Mais vous avez raison, point bien pris - j'ai supprimé la citation.
gst
1
Merci. Le fil de discussion du python-dev était révélateur. En fait, la fonctionnalité a déjà été implémentée dans python 3.8, qui a été publié il y a seulement deux jours (14 octobre 2019).
normanius
La citation de docs n'est pas utile, elle pose seulement la question suivante "alors, pourquoi le type dict n'implémente-t-il pas __reversed__"? Le lien python-dev a un contenu utile, mais les parties pertinentes doivent être reproduites directement dans la réponse (car ces liens hors site ont tendance à pourrir).
wim
1

Mise à jour pour python 3.8

Dict et dictviews sont désormais itérables dans l'ordre d'insertion inversé à l'aide de reverse ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>
Гончаров Дмитрий
la source
Cette réponse apporte-t-elle quelque chose de nouveau qui n'est pas déjà couvert par d'autres réponses, commentaires ou la question elle-même?
normanius
@normanius Il a un petit exemple de code visuel, pourrait être utile pour le navigateur rapide
jamylak