web-dev-qa-db-fra.com

Python 2.7 lance ValueError: list.remove (x): x pas dans la liste

Chaque fois que j'exécute ce programme, j'obtiens cette erreur:

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

J'essaie de réduire la santé d'un seul étranger chaque fois qu'il est touché par un boulon. Cet étranger unique devrait également être détruit si sa santé est de <= 0. De même, le verrou serait également détruit. Voici mon code:

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 se produit sur la ligne aliens.remove(a). Juste pour clarifier, les aliens et bolts sont des listes de dictionnaires.

Qu'est-ce que je fais mal?

14
Remolten

Vous ne devez pas supprimer des éléments d'une liste que vous parcourez. Créez plutôt une copie:

for a in aliens[:]:

et

for b in bolts[:]:

La modification d'une liste tout en bouclant dessus affecte la boucle:

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

La suppression d'éléments d'une liste que vous parcourez deux fois rend les choses encore plus compliquées, ce qui entraîne une erreur 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

Lorsque vous créez une copie des listes que vous modifiez au niveau chaque de vos boucles, vous évitez le problème:

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

En cas de collision, il suffit de retirer le boulon bne fois, pas dans la boucle où vous blessez les extraterrestres. Nettoyez les extraterrestres séparément plus tard:

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
20
Martijn Pieters

Il y a un bogue dans votre code qui cause cela. Votre code, simplifié, ressemble à:

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

Cela vous oblige à faire une boucle sur aliens plusieurs fois pour chaque entrée dans b. Si le b est supprimé sur la première boucle sur aliens alors, quand il boucle dessus une deuxième fois, vous obtiendrez une erreur.

Quelques choses à corriger. Commencez par changer la boucle interne sur aliens pour utiliser autre chose que a, donc:

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

Deuxièmement, ne supprimez b de bolts qu'une seule fois. alors:

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)

Il y a aussi d'autres problèmes avec ce code, je pense, mais c'est la cause de votre problème principal. Le message de Martijn peut également aider.

2
dave mankoff

Donnez également aux boulons une "santé", initialisée à 1. Ensuite, vous pouvez faire une boucle imbriquée pour calculer tous les dégâts, et deux "boucles" non imbriquées distinctes pour supprimer tout ce qui est "mort". Sauf, ne le faites pas tout à fait comme ça, car vous ne voulez toujours pas modifier la liste que vous parcourez. Faire une copie est encore trop compliqué. Ce que vous voulez vraiment faire est directement construire une nouvelle liste uniquement des choses encore "vivantes", et vous pouvez le faire de manière descriptive avec des compréhensions de liste (ou comme indiqué ici, avec 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))
0
Karl Knechtel