Python throws ValueError: list.remove(x): x not in list

You should not remove items from a list you are looping over. Create a copy instead:

for a in aliens[:]:

and

for b in bolts[:]:

Modifying a list while looping over it, affects the loop:

>>> lst = [1, 2, 3]
>>> for i in lst:
...     print i
...     lst.remove(i)
... 
1
3
>>> lst
[2]

Removing items from a list you are looping over twice makes things a little more complicated still, resulting in a ValueError:

>>> lst = [1, 2, 3]
>>> for i in lst:
...     for a in lst:
...         print i, a, lst
...         lst.remove(i)
... 
1 1 [1, 2, 3]
1 3 [2, 3]
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
ValueError: list.remove(x): x not in list

When creating a copy of the lists you are modifying at each level of your loops, you avoid the problem:

>>> lst = [1, 2, 3]
>>> for i in lst[:]:
...     for i in lst[:]:
...         print i, lst
...         lst.remove(i)
... 
1 [1, 2, 3]
2 [2, 3]
3 [3]

When you have a collision, you only need to remove the b bolt once, not in the loop where you hurt the aliens. Clean out the aliens separately later:

def manage_collide(bolts, aliens):
    for b in bolts[:]:
        for a in aliens:
            if b['rect'].colliderect(a['rect']) and a['health'] > 0:
                bolts.remove(b)
                for a in aliens:
                    a['health'] -= 1
    for a in aliens[:]:
        if a['health'] <= 0:
            aliens.remove(a)
    return bolts, aliens

There is a bug in your code that is causing this. Your code, simplified, looks like:

for b in bolts:
  for a in aliens:
    for a in aliens:
      bolts.remove(b)

That is causing you to loop over aliens multiple times for every entry in b. If the b is removed on the first loop over aliens then, when it loops over it a second time, you will get there error.

A few things to fix. First, change in the inner loop over aliens to use something other than a, so:

for b in bolts:
  for a in aliens:
    for c in aliens:
      if hit:
        bolts.remove(b)

Second, only remove b from bolts once. so:

for b in bolts:
  for a in aliens:
    should_remove = False
    for c in aliens:
      if hit:
        should_remove = True
    if should_remove:
      bolts.remove(b)

There are other issues with this code as well, I think, but that is the cause your main problem. Martijn's post may also help.

Tags:

Python