Python 2.7 lanza ValueError: list.remove (x): x no está en la lista

Cada vez que ejecuto este progtwig, me sale este error:

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

Estoy tratando de disminuir la salud de un solo alienígena cada vez que es golpeado por un perno. Ese alienígena individual también debería ser destruido si su salud es <= 0 . Del mismo modo, el cerrojo también sería destruido. Aquí está mi código:

 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 

El ValueError sucede en la línea aliens.remove(a) . Solo para aclarar, tanto los aliens como los bolts son listas de diccionarios.

¿Qué estoy haciendo mal?

No debe eliminar los elementos de una lista que esté repasando. Crear una copia en su lugar:

 for a in aliens[:]: 

y

 for b in bolts[:]: 

La modificación de una lista mientras se realiza un bucle sobre ella, afecta al bucle:

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

La eliminación de elementos de una lista en la que se repite el ciclo hace que las cosas sean un poco más complicadas, lo que resulta en un 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 "", line 4, in  ValueError: list.remove(x): x not in list 

Al crear una copia de las listas que está modificando en cada nivel de sus bucles, evita el problema:

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

Cuando tienes una colisión, solo necesitas quitar el perno b una vez , no en el circuito donde lastimas a los alienígenas. Limpie los alienígenas por separado más tarde:

 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 

Hay un error en su código que está causando esto. Su código, simplificado, se parece a:

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

Eso hace que pases por los aliens varias veces por cada entrada en b . Si la b se elimina en el primer bucle sobre aliens , entonces, cuando se vuelva sobre él por segunda vez, obtendrá un error.

Algunas cosas para arreglar Primero, cambie el bucle interno sobre los aliens para usar algo que no sea a , así que:

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

Segundo, solo quite b de los bolts una vez. asi que:

 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) 

También hay otros problemas con este código, creo, pero esa es la causa de su problema principal. La publicación de Martijn también puede ayudar.

También le da a los tornillos una “salud”, inicializada a 1. Luego puede hacer un ciclo nested para calcular todo el daño, y dos “ciclos” no nesteds separados para eliminar todo lo que está “muerto”. Excepto, no lo hagas así, porque aún no quieres modificar la lista sobre la que estás repitiendo. Hacer una copia es todavía demasiado complicado. Lo que realmente desea hacer es crear directamente una nueva lista de las cosas que aún están “vivas” , y puede hacerlo de forma descriptiva con la lista de comprensión (o como se muestra aquí, con 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))