Python 2.7 выбрасывает ValueError: list.удалить(x): x не в списке

каждый раз, когда я запускаю программу, я получаю эту ошибку:

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

Я пытаюсь снизить здоровье одного инопланетянина, когда в него попадает болт. Что один иностранец должен быть уничтожен, если его здоровье <= 0. Точно так же и болт будет уничтожен. Вот мой код:

def manage_collide(bolts, aliens):
    # Check if a bolt collides with any alien(s)
    for b in bolts:
        for a in aliens:
            if b['rect'].colliderect(a['rect']):
                for a in aliens:
                    a['health'] -= 1
                    bolts.remove(b)
                    if a['health'] == 0:
                        aliens.remove(a)
    # Return bolts, aliens dictionaries
    return bolts, aliens

на ValueError происходит на строку aliens.remove(a). Просто чтобы уточнить, оба aliens и bolts списки словарей.

что я делаю не так?

3 ответов


вы не должны удалять элементы из списка, вы не обходишь. Вместо этого создайте копию:

for a in aliens[:]:

и

for b in bolts[:]:

изменение списка во время цикла над ним влияет на цикл:

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

удаление элементов из списка, который вы зацикливаетесь дважды, делает вещи немного более сложными, что приводит к 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

при создании копии списков, которые вы изменяете в каждого уровень ваших петель, вы избегайте проблемы:

>>> 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]

когда у вас есть столкновение, вам нужно только удалить b болт после, не в петле, где вы повредили инопланетян. Очистите пришельцев отдельно позже:

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

в вашем коде есть ошибка, которая вызывает это. Ваш код, упрощенный, выглядит так:

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

это заставляет вас зацикливаться aliens несколько раз для каждой записи в b. Если b удаляется в первом цикле через aliens затем, когда он повторит его во второй раз, вы получите там ошибку.

несколько вещей, чтобы исправить. Во-первых, изменение внутреннего цикла over aliens использовать что-то другое, чем a, так:

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

дайте болтам также "здоровье", инициализированное до 1. Затем вы можете сделать один вложенный цикл, чтобы вычислить все повреждения, и два отдельных неназванных "цикла", чтобы удалить все, что "мертво". Кроме того, не делайте этого совсем так, потому что вы все еще не хотите изменять список, который вы зацикливаете. Сделать копию все еще слишком сложно. Что вы действительно хотите сделать это прямо создайте новый список только еще "живых" вещей, и вы можете сделать это описательно со списком понимания (или, как показано здесь, с filter).

# for example
class Alien:
    # ... other stuff
    def damage(self): self.hp -= 1
    def alive(self): return self.hp > 0

# similarly for Bolt

def collide(an_alien, a_bolt):
    # etc.

def handle_collisions(aliens, bolts):
    for a in aliens:
        for b in bolts:
            if collide(a, b):
                a.damage()
                b.damage()

    return list(filter(Alien.alive, aliens)), list(filter(Bolt.alive, bolts))