关于python:循环遍历嵌套列表时,我不断收到“ValueError:list.remove(x):x not in list”

I keep getting “ValueError: list.remove(x): x not in list” when looping through nested lists

本问题已经有最佳答案,请猛点这里访问。

我在试着做一个简单的太空入侵者版本,我总是遇到同样的错误。

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

试图在入侵者被击中后将其清除。

这是密码。

1
2
3
4
5
6
7
8
def killEnemies(bullets, enemies):
    for bullet in bullets:
        for x in enemies:
            for y in x:
                if bullet.top <= y.bottom and bullet.top >= y.top:
                    if bullet.left >= y.left and bullet.right <= y.right:
                        x.remove(y)
                        bullets.remove(bullet)

只有当if语句为true时才会出现问题,并且控制台说错误发生在最后一行

这是剩下的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import pygame, sys
from pygame.locals import *


pygame.init()

FPS = 30
fpsClock = pygame.time.Clock()

DISPLAYSURF = pygame.display.set_mode((800, 660), 0, 32)
pygame.display.set_caption('Space Invaders')

white = (255, 255, 255)
black = (0, 0, 0)
shipx = 50
shipy = 630
DISPLAYSURF.fill(black)

timer = fpsClock.tick()
time = 0
direction = ''
bullets = []
bulletx = shipx + 25
bullety = shipy - 50
enemies = [[], [], [], [], [], [], []]
shields = []


def drawEnemies(enemies):
    y = 0
    for n in enemies:
        x = 0
        for f in range(7):
            enemy = pygame.draw.rect(DISPLAYSURF, white, (30 + x, 40 + y, 75, 20))
            n.append(enemy)
            x += 110
        y += 30
    return enemies

def killEnemies(bullets, enemies):
    for bullet in bullets:
        for x in enemies:
            for y in x:
                if bullet.top <= y.bottom and bullet.top >= y.top:
                    if bullet.left >= y.left and bullet.right <= y.right:
                    x.remove(y)
                    bullets.remove(bullet)


def moveBullets(bullets):
    for bullet in bullets:
         bullet.top -= 15
    for b in bullets:
        pygame.draw.rect(DISPLAYSURF, white, b)

while True:


    if direction == 'left':
        shipx -= 8
        bulletx -= 8
    elif direction == 'right':
        shipx += 8
        bulletx += 8


    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        key = pygame.key.get_pressed()

        if key[K_LEFT]:
            direction = 'left'
        elif key[K_RIGHT]:
            direction = 'right'
        elif key[K_SPACE]:
            bullet = pygame.draw.line(DISPLAYSURF, white, (bulletx, bullety), (bulletx, bullety - 25), 2)
            bullets.append(bullet)

        if event.type == KEYUP:
            direction = ''

    time += timer        
    DISPLAYSURF.fill(black)
    pygame.draw.polygon(DISPLAYSURF, white, ((shipx, shipy), (shipx + 25, shipy - 50), (shipx + 50, shipy)), 1)
    drawEnemies(enemies)
    moveBullets(bullets)
    killEnemies(bullets, enemies)
    pygame.display.update()
    fpsClock.tick(FPS)


在迭代列表时,不应该尝试改变它。这是你问题的根源。

对于一个非常简单的示例,请尝试运行:

1
2
3
4
some_list = [1,2,3,4]
for x in some_list:
    some_list.remove(x)
print(some_list) # Prints [2, 4]

显然不是预期的结果——您可能期望整个列表是空的。

解决方案是使用一个列表理解来创建一个只包含您所需元素的新列表,或者复制一个列表进行迭代。如果您按照创建副本的路线进行操作,这很简单:

1
2
3
4
5
6
7
8
def killEnemies(bullets, enemies):
    for bullet in bullets[:]:
        for x in enemies:
            for y in x[:]:
                if bullet.top <= y.bottom and bullet.top >= y.top:
                    if bullet.left >= y.left and bullet.right <= y.right:
                        x.remove(y)
                        bullets.remove(bullet)

注意,list[:]创建了列表的浅副本,因此原始列表和复制列表中的基础对象应该相同。

另一个问题是for循环的逻辑。对于每一个子弹,您都要迭代多个敌人,并且需要在每一个子弹"用完"后从两个内部循环中分离出来。如前所述,您似乎要多次删除每个项目符号。我建议按照以下顺序进行代码重构:

1
2
3
4
5
6
7
8
9
10
11
12
def killEnemies(bullets, enemies):
    for bullet in bullets[:]:
        iterEnemies(bullets, bullet, enemies)

def iterEnemies(bullets, bullet, enemies):
    for x in enemies:
        for y in x[:]:
            if bullet.top <= y.bottom and bullet.top >= y.top:
                if bullet.left >= y.left and bullet.right <= y.right:
                    x.remove(y)
                    bullets.remove(bullet)
                    return