2008-12-04 17 views
12

Les méthodes dict dict.keys(), dict.items() et dict.values ​​() renvoient des "vues" au lieu de listes. http://docs.python.org/dev/3.0/whatsnew//3.0.htmlPython 3.0 - les méthodes dict retournent des vues - pourquoi?

tout d'abord comment est une vue différente d'un iterator? Deuxièmement, quel est l'avantage de ce changement? Est-ce juste pour des raisons de performance?

Cela ne me semble pas intuitif, c'est-à-dire que je demande une liste de choses (donnez-moi toutes vos clés) et je récupère autre chose. Cela va-t-il dérouter les gens?

+0

Ceci est une excellente réponse sur StackOverflow: http://stackoverflow.com/questions/8957750/what-are-python-dictionary-view-objects – Exthen

+0

On dirait que l'url est mort. – Borealis

Répondre

13

Vous obtenez effectivement une liste. Ce n'est simplement pas une copie de la liste interne, mais quelque chose qui agit comme s'il s'agissait d'une liste mais qui ne représente que l'état interne.

C'est de la même manière qu'il est implémenté en Java (et probablement aussi dans beaucoup d'autres langages/environnements).

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

Si vous voulez simplement parcourir les touches, il n'est pas nécessaire de créer une nouvelle liste. Et si vous en avez vraiment besoin en tant que liste séparée (en tant que copie), vous pouvez facilement créer cette liste à partir de la vue.

6

La réponse de Joachim Sauer explique très bien pourquoi un list n'est pas retourné. Mais ceci laisse la question de savoir pourquoi ces fonctions ne retourneraient pas les itérateurs, tout comme iteritems etc. en Python 2.

Un itérateur est beaucoup plus restrictif qu'un conteneur. Par exemple, un itérateur n'autorise pas plus d'un passage; Si vous essayez un second passage, vous trouverez qu'il est vide. Par conséquent, les opérations telles que elem in cont sont prises en charge par les conteneurs, mais ne peuvent pas être prises en charge par les itérateurs: une fois que vous vérifiez si un élément est "dans" l'itérateur, l'itérateur est détruit! D'autre part, obtenir un conteneur habituellement nécessite d'effectuer une copie telle que la création d'une liste à partir des clés du dictionnaire. L'objet view a le meilleur des deux mondes: il se comporte comme un conteneur et ne fait pas de copie du dictionnaire! C'est, en fait, une sorte de conteneur virtuel en lecture seule qui fonctionne en liant le dictionnaire sous-jacent. Je ne sais pas si c'est vu ailleurs dans le standard Python.

Edit:

@AntonyHatchkins: la raison pour laquelle il ne retourne pas une fonction de générateur est qu'il ne permettrait pas une opération in rapide. Oui, in fonctionne pour les fonctions du générateur (quand vous les appelez). Autrement dit, vous pouvez le faire:

def f(): 
    for i in range(10): 
    yield i 

5 in f() # True 

Mais selon la définition de in, si le côté droit est un générateur, python passera par tous les n éléments du générateur - conduisant à O(n) la complexité du temps. Il n'y a rien que vous puissiez faire parce que c'est le seul comportement significatif un générateur arbitraire.D'autre part, dans le cas de l'affichage du dictionnaire, vous pouvez implémenter in comme vous le souhaitez, car vous en savez plus sur les données que vous gérez. Et en fait in est mis en œuvre avec O(1) complexité en utilisant une table de hachage. Vous pouvez le vérifier en exécutant

>>> d = dict(zip(range(50000000), range(50000000))) 
>>> 49999999 in d 
True 
>>> 49999999 in iter(d) # kinda how generator function would work 
True 
>>> 

et en remarquant à quelle vitesse la première in est comparée à la seconde in.

+0

Puis va une autre question pourquoi ne retournerait pas les fonctions du générateur. Ce sont des entités à plusieurs passages (prenant ainsi en charge les opérations 'elem in cont') et ne prennent pas beaucoup de mémoire. –

+0

@AntonyHatchkins a édité la réponse pour expliquer la raison – max

+0

Merci, je l'ai appris moi-même depuis lors, mais il pourrait être utile pour d'autres personnes aussi. –

0

Comme cela a déjà été mentionné dans la question connexe, la vue a la méthode len(), qui manque à l'itérateur (encore la liste en possède). Un autre avantage de renvoyer une vue au lieu d'une liste est qu'au moins pour les clés, elle a un test d'appartenance optimisé dans les opérations O (1) au lieu de O (N) pour la liste (ou l'itérateur).