-2

I want to remove specific items from python list by iterating and checking if it meet some requirements. At first, I just operate on a list of customized class objects, but it actually meet some errors, and I experiment on a python list of primitive type int, just to find strange result!

Here is some code excerpts:

>>> a=[1,2,3,4,5]
>>> for i in a:
...     a.remove(i)
... 
>>> a
[2, 4]

I expect the a should be [] after the loop, but it proves to be [2,4], I wonder why actually. I found a related question in Remove items from a list while iterating, but it only gives a solution on how to remove specific items, not concerning the mechanism actually. I really want to know the reason of this strange result.

martineau
  • 119,623
  • 25
  • 170
  • 301
Shore Shi
  • 1
  • 2

3 Answers3

0

lets try to print the values of i and a while iterating.

for i in a:
     print i, a
     a.remove(i)

the output will be:

1 [1, 2, 3, 4, 5]
3 [2, 3, 4, 5]
5 [2, 4, 5]

so when you remove an element, the indices will change, so while value at index 1 was 2 earlier, now it is 3. This will be the value of i.

Salmaan P
  • 817
  • 1
  • 12
  • 32
0

So you've exposed a little of the python implementation. Basically, an array powers the python list, and it is simply incrementing the array index by 1. So it'll go to a[0], a[1], a[2]... and check before each iteration that it's not gonna run off the end of the array. As you remove the first item '1' from the list, '2' moves to a[0]. The array now looks like [2,3,4,5]. The iterator is now pointing to a[1], so now '3' gets removed. Finally, skipping over '4', '5' gets removed.

a = [1,2,3,4,5]

for i in a:
  print("a:%s i=%s"%(a,i))
  a.remove(i)

print("final a: %s"%a)

Gives the output

a:[1, 2, 3, 4, 5] i=1
a:[2, 3, 4, 5] i=3
a:[2, 4, 5] i=5
final a: [2, 4]

Here's the real nuts and bolts if you're interested. https://github.com/python/cpython/blob/master/Objects/listobject.c#L2832

djcrabhat
  • 464
  • 5
  • 10
-1

The reason your solution doesn't work as expected is because the iterator doesn't behave the way you'd expect if the list is modified. If you're example was rewritten this way, you'd get the result you expect.

>>> a=[1,2,3,4,5]
>>> b = a[:]
>>> for i in b:
...     a.remove(i)
... 
>>> a
[]

This is because 'b' is a copy of 'a', so be doesn't get modified when a does. This means the iterator doesn't have the data structure modified underneath it.

A more efficient solution is:

a = [1,2,3,4,5]
a = [i for i in a if not condition(i)]

This list comprehension copies as it goes through the source list, and only bothers to copy the elements that aren't being removed.

DonGar
  • 7,344
  • 8
  • 29
  • 32