2013-08-20 4 views
2

Je suis tombé sur un comportement très étrange en Python. En utilisant une classe dérivée de UserDict, l'itérateur a.items() se comporte différemment dans une boucle de a.data.items(), même si les deux sont identiques:Le même objet itérateur donne un résultat différent dans la boucle for?

Python 3.3.1 (default, Apr 17 2013, 22:32:14) 
[GCC 4.7.3] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from datastruct import QueueDict 
>>> a=QueueDict(maxsize=1700) 
>>> for i in range(1000): 
...  a[str(i)]=1/(i+1) 
... 
>>> a.items() 
ItemsView(OrderedDict([('991', 0.0010080645161290322), ('992', 0.0010070493454179255), ('993', 0.001006036217303823), ('994', 0.0010050251256281408), ('995', 0.001004016064257028), ('996', 0.0010030090270812437), ('997', 0.001002004008016032), ('998', 0.001001001001001001), ('999', 0.001)])) 
>>> a.data.items() 
ItemsView(OrderedDict([('991', 0.0010080645161290322), ('992', 0.0010070493454179255), ('993', 0.001006036217303823), ('994', 0.0010050251256281408), ('995', 0.001004016064257028), ('996', 0.0010030090270812437), ('997', 0.001002004008016032), ('998', 0.001001001001001001), ('999', 0.001)])) 
>>> a.items()==a.data.items() 
True 
>>> # nevertheless: 
... 
>>> for item in a.items(): print(item) 
... 
('992', 0.0010070493454179255) 
>>> for item in a.data.items(): print(item) 
... 
('993', 0.001006036217303823) 
('994', 0.0010050251256281408) 
('995', 0.001004016064257028) 
('996', 0.0010030090270812437) 
('997', 0.001002004008016032) 
('998', 0.001001001001001001) 
('999', 0.001) 
('991', 0.0010080645161290322) 
('992', 0.0010070493454179255) 
>>> 

La définition de classe est comme suit:

import collections, sys 

class QueueDict(collections.UserDict): 

    def __init__(self, maxsize=1*((2**10)**2), *args, **kwargs): 
     self._maxsize=maxsize 
     super().__init__(*args, **kwargs) 
     self.data=collections.OrderedDict(self.data) 

    def __getitem__(self, key): 
     self.data.move_to_end(key) 
     return super().__getitem__(key) 

    def __setitem__(self, key, value): 
     super().__setitem__(key, value) 
     self._purge() 

    def _purge(self): 
     while sys.getsizeof(self.data) > self._maxsize: 
      self.data.popitem(last=False) 

C'est assez dérangeant. Des idées comment le même objet [par inspection "visuelle", et aussi par (a.items()==a.data.items()) == True] peut, et pourquoi il le fait, se comporter différemment dans la boucle for?

Merci pour votre aide et vos idées!

+0

Quelle est exactement la différence que vous voyez ici? –

+0

Exactement quelle est la différence entre l'utilisation des deux objets dans une boucle for? – NPE

+0

@DanielRoseman: le premier seulement des itérations sur le premier élément, le deuxième sur tout! –

Répondre

2

La modification d'une collection pendant l'itération peut avoir (et dans ce cas, avoir) des conséquences inattendues.

Votre getter;

def __getitem__(self, key): 
    self.data.move_to_end(key) 
    return super().__getitem__(key) 

... déplace la clé en cours à la fin de la collecte, et qui fera la boucle sur a.items arrêt, car il pense qu'il a atteint la fin de la collection. Commenter la ligne move_to_end permet à l'itération de s'exécuter comme prévu.

Lorsque vous itérez sur a.data.items, votre getter n'est jamais invoqué, donc il n'y a pas de problème.

+0

Merci pour la réponse très perspicace !, cependant: après avoir écrasé la méthode '__items__' dans la classe comme:' def __items __ (self): retourne self.data.items() ', je reçois toujours les symptômes ci-dessus (sortie) . À ma connaissance, dans ce cas, le getter 'QueueDict' n'est pas appelé, seulement celui de' QueueDict.data', qui est le OrderedDict. Des idées? –

+1

@ASz Un point d'arrêt dans '__items __()' semble ne jamais être touché, je ne trouve pas de documentation sur cette fonction interne, voulez-vous dire '__iter __()'? –

+0

Oh, bon sang, c'est la simple méthode 'items()' que je dois remplacer. J'ai mélangé 'items()' avec '__iter __()' dans ma carte mentale. En surchargeant 'def items (self): retourne self.data.items()' la boucle for fonctionne comme prévu! Merci, Joachim! –

Questions connexes