4

I was trying out some things with lists in the interactive interpreter and I noticed this:

>>> list = range(1, 11)
>>> for i in list:
...     list.remove(i)
...
>>> list
[2, 4, 6, 8, 10]

Can anyone explain why it left even numbers? This is confusing me right now... Thanks a lot.

Bridgo
  • 149
  • 5
  • `for i in list[:]` or `for i in tuple(list)` should solve your problem, since either the first one will make a slice copy, or the second one will make a new immutable object (tuple). – John Doe Oct 26 '11 at 22:46
  • 1
    Standard warning: it's a bad habit to name your lists "list" because that clobbers the builtin type list. – DSM Oct 26 '11 at 22:47
  • Good point! :) I didn't think about it when I named it "list". (Probably had to do with the author naming his "list") – John Doe Oct 26 '11 at 22:48
  • 3
    possible duplicate of [Removing Item From List - during iteration - what's wrong with this idiom?](http://stackoverflow.com/questions/2896752/removing-item-from-list-during-iteration-whats-wrong-with-this-idiom) – Winston Ewert Oct 26 '11 at 22:55
  • You've committed one of the classic blunders! Don't modify a list while iterating over it. – PaulMcG Oct 27 '11 at 05:59

4 Answers4

7

It isn't safe to modify a list that you are iterating over.

ObscureRobot
  • 7,306
  • 2
  • 27
  • 36
4

My guess is that the for loop is implemented like the following:

list = range(1, 11)

i = 0
while i < len(list):
    list.remove(list[i])
    i += 1

print(list)

Every time an element is removed, the "next" element slides into its spot, but i gets incremented anyway, skipping 2 elements.

But yes, ObscureRobot is right, it's not really safe to do this (and this is probably undefined behavior).

Owen
  • 38,836
  • 14
  • 95
  • 125
  • @Paul Sorry I meant that the for loop code was undefined, at least according to the Python docs – Owen Oct 27 '11 at 07:14
3

If you want to modify a list whilst iterating over it, work from back to front:

lst = range(1, 11)
for i in reversed(lst):
    lst.remove(i)
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • @Bridgo. If you remove an item from the front of the list, all the other items have to move down; but if you remove from the back, the other items are left unchanged. – ekhumoro Oct 26 '11 at 22:59
  • Ahh.. Thanks lot. That makes a lot of sense. – Bridgo Oct 26 '11 at 23:01
  • The nice thing about `reversed()` is that it doesn't even create a copy of the list, like some recommend doing. – kindall Oct 27 '11 at 02:37
2

I find this easiest to explain using Python:

>>> for iteration, i in enumerate(lst):
...     print 'Begin iteration', iteration, 'where lst =', str(lst), 'and the value at index', iteration, 'is', lst[iteration]
...     lst.remove(i)
...     print 'End iteration', iteration, 'where lst =', str(lst), 'with', i, 'removed\n'
... 
Begin iteration 0 where lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and the value at index 0 is 1
End iteration 0 where lst = [2, 3, 4, 5, 6, 7, 8, 9, 10] with 1 removed

Begin iteration 1 where lst = [2, 3, 4, 5, 6, 7, 8, 9, 10] and the value at index 1 is 3
End iteration 1 where lst = [2, 4, 5, 6, 7, 8, 9, 10] with 3 removed

Begin iteration 2 where lst = [2, 4, 5, 6, 7, 8, 9, 10] and the value at index 2 is 5
End iteration 2 where lst = [2, 4, 6, 7, 8, 9, 10] with 5 removed

Begin iteration 3 where lst = [2, 4, 6, 7, 8, 9, 10] and the value at index 3 is 7
End iteration 3 where lst = [2, 4, 6, 8, 9, 10] with 7 removed

Begin iteration 4 where lst = [2, 4, 6, 8, 9, 10] and the value at index 4 is 9
End iteration 4 where lst = [2, 4, 6, 8, 10] with 9 removed

Note that it's a bad idea to (a) modify a list while iterating over it and (b) call a list "list".

johnsyweb
  • 136,902
  • 23
  • 188
  • 247