2013-11-23 4 views
2

У меня есть ситуация, когда цикл for вне класса взаимодействует с перечислением внутри класса.Python enumerate in for loop пропускает альтернативные элементы

Пример кода:

class MyContainerClass(object): 
    def __init__(self): 
     self.collection = [] 
    def __str__(self): 
     return (len(self.collection) == 0 and 'Empty' or 
       ', '.join(str(item) for item in self.collection)) 
    def __iadd__(self,item): 
     print "adding {item}".format(item=str(item)) 
     self.collection.append(item) 
     return self 
    def __iter__(self): 
     return iter(self.collection) 
    def discard(self,item): 
     print "discarding {item}".format(item=str(item)) 
     for index, value in enumerate(self.collection): 
      if value == item: 
       print "found {value}=={item}".format(value=value,item=item) 
       return self.collection.pop(index) 
     return False 

class MyItemClass(object): 
    def __init__(self,value): 
     self.value=value 
    def __str__(self): 
     return '{value}'.format(value=self.value) 
    def __eq__(self,other): 
     if self.value == other.value: 
      return True 
     else: 
      return False 

c1 = MyContainerClass() 
c2 = MyContainerClass() 

c2 += MyItemClass('item1') 
c2 += MyItemClass('item2') 
c2 += MyItemClass('item3') 
c2 += MyItemClass('item4') 
c2 += MyItemClass('item5') 
print "c1 is : {c1}".format(c1=str(c1)) 
print "c2 is : {c2}".format(c2=str(c2)) 

for item in c2: 
    print "for got {item}".format(item=str(item)) 
    c1 += c2.discard(item) 

print "c1 is : {c1}".format(c1=str(c1)) 
print "c2 is : {c2}".format(c2=str(c2)) 

Производит этот вывод:

adding item1 
adding item2 
adding item3 
adding item4 
adding item5 
c1 is : Empty 
c2 is : item1, item2, item3, item4, item5 
for got item1 
discarding item1 
found item1==item1 
adding item1 
for got item3 
discarding item3 
found item3==item3 
adding item3 
for got item5 
discarding item5 
found item5==item5 
adding item5 
c1 is : item1, item3, item5 
c2 is : item2, item4 

я уверен, что это что-то действительно очевидно, возможно сделать с помощью функции ITER, но я не могу видеть это на данный момент.

ответ

0

Вы вносите изменения c2.collectiondiscard) одновременно с итерацией по нему (for item in c2:). Не делай этого.

+0

Конечно ... Так что добавление __getitem__ в класс контейнера и использование среза - самый чистый способ достичь этого? – PhilipDG

+0

@PhilipDG: Лично я просто написал 'для элемента в кортеже (c2):'. Но если вы обычно делаете это для списка, это написать 'для элемента в c [:]' then yes, реализовать '__getitem__' для обработки срезов. Я думаю, это вопрос о том, как вы похожи на список «MyContainerClass». –

0

Небезопасно модифицировать контейнер, пока вы его итерации.

В этом случае происходит то, что итерация отслеживает количество пройденных элементов. Поэтому, когда вы вынимаете item1, итератор знает, что он уже прошел первый элемент в контейнере ... который теперь item2. Таким образом, он с радостью продолжается до item3.