Que sont les objects de vue de dictionnaire?

En python 2.7, nous avons les méthodes d’affichage du dictionnaire disponibles.

Maintenant, je connais les avantages et les inconvénients de ce qui suit:

  • dict.items() (et values , keys ): renvoie une liste pour que vous puissiez réellement stocker le résultat, et
  • dict.iteritems() (et similaires): retourne un générateur, vous pouvez donc parcourir chaque valeur générée une par une.

Que sont dict.viewitems() (et autres) pour? Quels sont leurs avantages? Comment ça marche? Qu’est-ce qu’une vue après tout?

J’ai lu que la vue reflète toujours les modifications du dictionnaire. Mais comment se comporte-t-il du sharepoint vue de la perf et de la mémoire? Quels sont les avantages et les inconvénients?

Les vues de dictionnaire sont essentiellement ce que leur nom dit: les vues sont simplement comme une fenêtre sur les clés et les valeurs (ou éléments) d’un dictionnaire. Voici un extrait de la documentation officielle de Python 3:

 >>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500} >>> keys = dishes.keys() >>> values = dishes.values() >>> # view objects are dynamic and reflect dict changes >>> del dishes['eggs'] >>> keys # No eggs anymore! dict_keys(['sausage', 'bacon', 'spam']) >>> values # No eggs value (2) anymore! dict_values([1, 1, 500]) 

(L’équivalent de Python 2 utilise dishes.viewkeys() et dishes.viewvalues() .)

Cet exemple montre le caractère dynamic des vues : la vue des clés n’est pas une copie des clés à un moment donné, mais plutôt une simple fenêtre qui vous montre les clés; si elles sont modifiées, alors ce que vous voyez à travers la fenêtre change également. Cette fonctionnalité peut être utile dans certaines circonstances (par exemple, on peut travailler avec une vue sur les clés dans plusieurs parties d’un programme au lieu de recalculer la liste de clés actuelle chaque fois que cela est nécessaire) – notez que si les clés du dictionnaire sont modifiées En itérant sur la vue, le comportement de l’iterator n’est pas bien défini, ce qui peut entraîner des erreurs .

L’un des avantages est que, par exemple, les clés n’utilisent qu’une quantité de mémoire réduite et fixe et qu’elles requièrent une quantité de temps processeur réduite et fixe , car il n’ya pas de création de liste de clés (Python 2, par contre). crée souvent inutilement une nouvelle liste, citée par Rajendran T, qui prend la mémoire et le temps dans une quantité proportionnelle à la longueur de la liste). Pour continuer l’analogie de la fenêtre, si vous voulez voir un paysage derrière un mur, vous faites simplement une ouverture (vous construisez une fenêtre); copier les clés dans une liste correspondrait plutôt à la copie d’une copie du paysage sur votre mur: la copie prend du temps, de l’espace et ne se met pas à jour.

Pour résumer, les vues sont simplement… des vues (fenêtres) sur votre dictionnaire, qui affichent le contenu du dictionnaire même après modification. Ils offrent des fonctionnalités différentes de celles des listes: une liste de clés contient une copie des clés du dictionnaire à un moment donné, tandis qu’une vue est dynamic et beaucoup plus rapide à obtenir, car elle n’a pas à copier de données ( clés ou valeurs) pour être créé.

Comme vous l’avez mentionné, dict.items() renvoie une copie de la liste des paires (clé, valeur) du dictionnaire qui est inutile et dict.iteritems() renvoie un iterator sur les paires (clé, valeur) du dictionnaire.

Prenons maintenant l’exemple suivant pour voir la différence entre un interlocuteur de dict et une vue de dict

 >>> d = {"x":5, "y":3} >>> iter = d.iteritems() >>> del d["x"] >>> for i in iter: print i ... Traceback (most recent call last): File "", line 1, in  RuntimeError: dictionary changed size during iteration 

Alors qu’une vue vous montre simplement ce qu’il y a dans le dict. Ça ne fait rien si ça change:

 >>> d = {"x":5, "y":3} >>> v = d.viewitems() >>> v dict_items([('y', 3), ('x', 5)]) >>> del d["x"] >>> v dict_items([('y', 3)]) 

Une vue est simplement ce à quoi le dictionnaire ressemble maintenant. Après avoir supprimé une entrée, .items() aurait été obsolète et .iteritems() aurait .iteritems() une erreur.

Juste en lisant les docs, j’ai cette impression:

  1. Les vues sont “pseudo-set-like”, dans la mesure où elles ne prennent pas en charge l’indexation, ce que vous pouvez faire avec elles consiste à tester leur appartenance et à les parcourir (les clés et les éléments sont plus ” set-like “en ce sens qu’ils ne contiennent pas de doublons).
  2. Vous pouvez les stocker et les utiliser plusieurs fois, comme les versions de liste.
  3. Comme ils reflètent le dictionnaire sous-jacent, toute modification du dictionnaire modifiera la vue et changera presque certainement l’ordre de l’itération . Donc, contrairement aux versions de la liste, ils ne sont pas “stables”.
  4. Comme ils reflètent le dictionnaire sous-jacent, ils sont presque certainement de petits objects proxy. copier les clés / valeurs / éléments nécessiterait qu’ils regardent le dictionnaire original d’une certaine manière et le copient plusieurs fois lorsque des changements se produisent, ce qui serait une implémentation absurde. Donc, je m’attendrais à très peu de mémoire, mais l’access serait un peu plus lent que directement dans le dictionnaire.

Donc, je suppose que la clé de l’emploi est si vous gardez un dictionnaire et que vous répétez plusieurs fois ses clés / éléments / valeurs avec des modifications entre les deux. Vous pouvez simplement utiliser une vue, en tournant for k, v in mydict.iteritems(): dans for k, v in myview: Mais si vous parcourez le dictionnaire une seule fois, je pense que les versions iter sont toujours préférables.

Les méthodes d’affichage renvoient une liste (pas une copie de la liste, comparée à .keys() , .items() et .values() ), elle est donc plus légère, mais reflète le contenu actuel du dictionnaire.

A partir de Python 3.0 – les méthodes dict retournent des vues – pourquoi?

La raison principale est que pour de nombreux cas d’utilisation, le renvoi d’une liste complètement détachée est inutile et inutile. Cela nécessiterait de copier tout le contenu (qui peut être ou pas beaucoup).

Si vous souhaitez simplement parcourir les touches, la création d’une nouvelle liste n’est pas nécessaire. Et si vous en avez besoin en tant que liste séparée (sous forme de copie), vous pouvez facilement créer cette liste à partir de la vue.

Les vues vous permettent d’accéder à la structure de données sous-jacente, sans la copier. En plus d’être dynamic au lieu de créer une liste, l’une de leurs utilisations les plus utiles est in test. Disons que vous voulez vérifier si une valeur est dans le dict ou non (soit la clé ou la valeur).

L’option 1 consiste à créer une liste de clés à l’aide de dict.keys() , cela fonctionne, mais consum évidemment davantage de mémoire. Si le dict est très grand? Ce serait du gaspillage.

Avec les views vous pouvez itérer la structure de données réelle, sans liste intermédiaire.

Utilisons des exemples. J’ai un dict avec 1000 clés de chaînes et de chiffres aléatoires et k est la clé que je veux rechercher

 large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. } >>> len(large_d) 1000 # this is one option; It creates the keys() list every time, it's here just for the example timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000) 13.748743600954867 # now let's create the list first; only then check for containment >>> list_keys = large_d.keys() >>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000) 8.874809793833492 # this saves us ~5 seconds. Great! # let's try the views now >>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000) 0.08828549011070663 # How about saving another 8.5 seconds? 

Comme vous pouvez le constater, l’object de view itératif améliore considérablement les performances, réduisant ainsi la surcharge de mémoire. Vous devez les utiliser lorsque vous devez effectuer des opérations similaires à Set .

Note : je cours sur Python 2.7