1

I've found a python puzzle and can't find out why it works.

x = ['a','b','c']
for m in x:
     x.remove(m)

and after this loop x = ['b']. But why?

As far as I understand for keyword implicitly creates iterator for this list. Does .remove() calls __next__() method so b is skipped? I can't find any mentions of it but this is my best guess.

Paul
  • 6,641
  • 8
  • 41
  • 56
  • 2
    Because the pointers are not updated when you remove an element. The last index is kept through the iterations, when you mutate the elements get shifted. – Padraic Cunningham Oct 21 '16 at 15:22
  • 3
    This is not documented behaviour and is implementation specific. Avoid. It is discussed here: https://unspecified.wordpress.com/2009/02/12/thou-shalt-not-modify-a-list-during-iteration/ – cdarke Oct 21 '16 at 15:26
  • The iterator probably has an implementation similar to `i = 0;while i < len(self):yield self[i];i+=1`. That means that `'b'` slides from index `1` to index `0` when `'a'` is removed. `'b'` is missed on the next iteration because `i` has advanced to index `1` and yields the item `'c'`. – Steven Rumbalski Oct 21 '16 at 15:31

1 Answers1

2

Here you are iterating over the original list. On the first iteration, you removed the 0th index element i.e. a. Now, your list is as: ['b','c']. On the second iteration your for loop will access the value at index 1 but your index 1 has value c. So the c is removed. Hence resultant list will be ['b'].

In order to make it behave expectedly, iterate over the copy of the list, and remove the item from original list. For example:

x = ['a','b','c']
for m in list(x):  # <-- Here 'list(x)' will create the copy of list 'x'
                   # for will iterate over the copy
     x.remove(m)

# updated value of 'x' will be: []

Note: If it is not for demo purpose and you are using this code for emptying the list, efficient way of emptying the list will be:

del x[:]
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • 1
    This deserves a warning due to poor performance characteristics. Each removal from the front of the list will result in every remaining item being shifted (copied!) back one position. If the list is large the performance impact will be large. If your list is 5 items long that will be only 10 shifts. If it is 1000 items long it will be 499500 shifts. – Steven Rumbalski Oct 21 '16 at 15:41
  • Yes, that is True. If what you want is just to make your list empty. Do, `del x[:]`. Updating the answer – Moinuddin Quadri Oct 21 '16 at 15:46