2013-01-02 2 views
6

Jedes Mal, wenn ich dieses Programm ausführen, bekomme ich diesen Fehler:Python 2.7 wirft Valueerror: list.remove (x): x nicht in der Liste

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

Ich versuche, die Gesundheit eines einzigen Ausländer zu senken, wenn es wird von einem Bolzen getroffen. Dieser einzelne Alien sollte auch zerstört werden, wenn seine Gesundheit <= 0 ist. Ähnlich würde der Bolzen auch zerstört werden. Hier ist mein 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 

Die ValueError geschieht auf der Linie aliens.remove(a). Zur Verdeutlichung sind sowohl die aliens als auch die bolts Listen von Wörterbüchern.

Was mache ich falsch?

+0

ValueError auf welcher Linie? – asheeshr

+0

aliens.remove (a) –

+0

Für zukünftige Referenz ist das Problem mit diesem Code, dass ich über die Aliens-Liste zweimal geschleift, was einige Probleme verursacht, wenn Sie versuchen, aus der Liste zu entfernen. Das Entfernen des zweiten "für ein in Aliens" würde dieses Problem verhindern. –

Antwort

14

Sie sollten keine Elemente aus einer Liste entfernen, die Sie durchlaufen. Erstellen Sie eine Kopie statt:

for a in aliens[:]: 

und

for b in bolts[:]: 

eine Liste ändern, während sie über das Looping, wirkt sich auf die Schleife:

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

Entfernen von Objekten aus einer Liste, die Sie zweimal sind Schleifen über macht Dinge noch ein wenig komplizierter, was zu einem ValueError führt:

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

Wenn eine Kopie der Listen Erstellen Sie auf jeder Ebene Ihrer Schleifen ändern, vermeiden Sie das 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] 

Wenn Sie eine Kollision haben, müssen Sie nur die b Schraube einmal entfernen müssen, nicht in der Schleife, wo du die Aliens verletzt hast. Reinigen Sie die Aliens später separat:

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 
+1

Dies beantwortet nicht wirklich die Frage des OP. Es ist eigentlich kein Problem, Elemente aus einer Liste zu entfernen, während man darüber iteriert; Es kann nur zu unerwarteten Ergebnissen führen, wenn Sie nicht wissen, wie es funktioniert. – kindall

+0

Es setzt immer noch einen ValueError auf die selbe Zeile aliens.remove (a), wenn nur diese Zeile in eine Kopieliste geändert wird. –

+0

@Kindall: Es sind die doppelten Schleifen, die es umso lustiger machen und zu einem Wertfehler führen können. –

1

Es gibt einen Fehler in Ihrem Code, der dies verursacht. Ihr Code, vereinfacht wie folgt aussieht:

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

, die Sie Schleife über aliens mehrere Male für jeden Eintrag in b verursacht. Wenn das b in der ersten Schleife über aliens entfernt wird, wird es, wenn es ein zweites Mal durchlaufen wird, einen Fehler erhalten.

Ein paar Dinge zu beheben. Zuerst über aliens in der inneren Schleife ändert etwas anderes als a zu verwenden, so:

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

Zweitens nur b von bolts einmal entfernen. 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) 

Es gibt noch andere Probleme mit diesem Code als auch, glaube ich, aber das ist die Ursache Ihres Hauptproblem. Martijns Post kann auch helfen.

0

Geben Sie den Schrauben eine "Gesundheit", initialisiert auf 1.Dann können Sie eine verschachtelte Schleife ausführen, um den gesamten Schaden zu berechnen, und zwei separate, nicht verschachtelte "Schleifen", um alles zu entfernen, was "tot" ist. Aber machen Sie es nicht so, denn Sie möchten die Liste, die Sie durchlaufen, immer noch nicht ändern. Eine Kopie zu machen ist immer noch zu kompliziert. Was Sie wirklich tun möchten, ist direkt bauen Sie eine neue Liste von nur die immer noch "lebenden" Dinge, und Sie können das mit List Comprehensions deskriptiv (oder wie hier gezeigt, mit 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)) 
Verwandte Themen