Python报错:ValueError: list.remove(x): x不在列表中。

24
每次运行这个程序时,我都会出现以下错误:
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) 这一行。为了澄清,aliensbolts 都是字典列表。

我做错了什么?


哪一行出现了 ValueError? - asheeshr
将来参考,这段代码的问题在于我对外星人列表进行了两次循环,这会在尝试从列表中删除时导致一些问题。删除第二个“for a in aliens”可以避免这个问题。 - Remolten
6个回答

49

在循环列表时,不应该从中删除元素。相反,应该创建一个副本:

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

1
这实际上并没有回答楼主的问题。在迭代列表时删除其中的项并不是一个问题;只是如果你不知道它的工作原理,可能会产生意想不到的结果。 - kindall
当仅将该行更改为复制列表时,它仍会在相同的行上引发ValueError错误,即使使用了复制列表。 - Remolten
@kindall:双重循环使得它更有趣,但也可能导致值错误。 - Martijn Pieters
这对我仍然不起作用。也许你可以直接修改我的代码? - Remolten

4
你的代码里有一个错误导致了这个问题。你的代码简化后如下所示:
for b in bolts:
  for a in aliens:
    for a in aliens:
      bolts.remove(b)

这会导致你在每个b条目上循环多次aliens。如果在第一次循环aliens时移除了b,那么当它第二次循环时,你将会得到错误。

有几件事需要修复。首先,在内部循环中改变a以使用其他内容,例如:

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

其次,只需从bolts中删除b一次即可。因此:

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)

这段代码还存在其他问题,我认为这是导致主要问题的原因。Martijn的文章可能也有帮助。

2

你不能使用list.remove,应该使用del list[x]

因为当你使用remove删除元素时,需要用元素的名称而不是索引来指定要删除的元素,所以在代码运行时会出现错误(value error: x is not in list)。但是,如果我们使用del,则可以通过索引删除元素。无论元素的名称是什么,使用del都可以使代码正确运行。我希望我的解释能够解决这个问题。

想要更清楚地理解,请尝试以下代码。然后将del替换为remove再运行一次代码,你就会知道我的意思了。代码如下:

aliens = [[4,3,2,1],[4,3,2,1],[4,3,2,1]]
print(aliens)
bolts = [b for b in range(1,30)]
for b in bolts:
    del aliens[0][0]
    print(aliens) 
        if len(aliens[0]) == 0:
            del aliens[0]
    if len(aliens) == 0
                print("you win")
        break

虽然这段代码可能解决了问题,但是包括解释这段代码是如何解决问题的会有助于提高你的帖子质量,也可能会获得更多的赞。记住,你正在回答未来读者的问题,而不仅仅是现在提问的人。请编辑你的答案,添加解释并指出其局限性和假设条件。 - Brian61354270

0
给螺栓一个“健康”值,初始值为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))

0

我认为如果你将for循环替换为while循环 - 仅在从列表中删除的循环中 - 就可以解决它

像这样

lis = [1,2,3,4,5,6]
i=0
while i in range(len(lis)) :
    lis. remove(lis[i])

-1
我建议这样做:
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']):
                a['health'] -= 1
                bolts.remove(b)
            if a['health'] == 0:
                aliens.remove(a)
    # Return bolts, aliens dictionaries
    return bolts, aliens

2
请不要发布仅包含代码的答案。未来的读者会感激您解释为什么这个答案回答了问题,而不是从代码中推断出来。此外,由于这是一个旧的、回答得很好的问题,请解释它如何补充所有其他答案。 - Gert Arnold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接