为什么此列表索引超出范围?

时间:2018-09-11 17:36:04

标签: python

我在pygame方面迈出了第一步,而且我已经使该程序正常工作。但是,在遍历main()数百次之后,它抛出“列表索引超出范围”消息。列表增加到一定限制后不会更改大小,因此我不确定错误发生在哪里。

运行程序后,只需在显示屏上移动鼠标,就会画出一圈圆圈,并随着时钟的滴答声而增大。最终,它将崩溃。

除了发生错误的行外,我已经省略了所有注释,希望这将使查找原因更加容易。

Stacktrace:

    Traceback (most recent call last):
  File "C:\Users\Devo\AppData\Local\Programs\Python\Python37-32\test files\Psychedelic Circles.py", line 89, in <module>
    main()
  File "C:\Users\Devo\AppData\Local\Programs\Python\Python37-32\test files\Psychedelic Circles.py", line 49, in main
    drawCircles(snakeLength)
  File "C:\Users\Devo\AppData\Local\Programs\Python\Python37-32\test files\Psychedelic Circles.py", line 75, in drawCircles
    pygame.draw.circle(SCREEN, colorList[i], coords[i], abs(i-(len(coords))) * 5, 0)#### Why does the list index go out of range?
IndexError: list index out of range

     import pygame, sys, random
from pygame.locals import*
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
FUSCHIA = (255,   0, 240)
GRAY = (80, 80, 80)
YELLOW = (255, 255, 0)
ORANGE = (255, 127, 0)
BLUE = (0, 0, 255)
INDIGO = (75, 0, 130)
VIOLET = (148, 0, 211)

FPS = 20
WWIDTH  = 1000
WHEIGHT = 700


BIGBUTTON = pygame.Rect(0, 0, 1000, 700)
rainbowTuple = (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET)


def main():
    global SCREEN, FPS, colorList, coords, snakeLength
    pygame.init()
    clock = pygame.time.Clock()
    size = (WWIDTH, WHEIGHT)    
    SCREEN = pygame.display.set_mode(size)    
    pygame.display.set_caption('Psychedelic Circles')   
    colorList = []
    coords = []
    snakeLength = 50 ### Change this value to make the circles disappear more   quickly or slowly
    while True:
        clickedButton = None
        SCREEN.fill(GRAY)
        drawButtons()
        checkForQuit()        
        for event in pygame.event.get():
            if event.type == MOUSEMOTION: 
                mousex, mousey = event.pos
                clickedButton = getButtonClicked(mousex, mousey)
                if clickedButton == FUSCHIA:
                    sendCoords(mousex, mousey)

        drawCircles(snakeLength)        

        pygame.display.update()
        clock.tick(FPS)

def terminate():
    pygame.quit()
    sys.exit()

def sendCoords(x, y):
    coords.append((x, y))
    colorList.append(random.choice(rainbowTuple))

def checkForQuit():
    for event in pygame.event.get(QUIT):
        terminate()
    for event in pygame.event.get(KEYUP):
        if event.key == K_ESCAPE:
            terminate()
        pygame.event.post(event)

def drawButtons():
    pygame.draw.rect(SCREEN, FUSCHIA, BIGBUTTON)

def drawCircles(snakeLength):
    for i in range(len(coords)):
        pygame.draw.circle(SCREEN, colorList[i], coords[i], abs(i-(len(coords))) * 5, 0)#### Why does the list index go out of range?
        if i > snakeLength :   
            popList()

def popList():
    coords.pop(0)
    colorList.pop(0)

def getButtonClicked(x, y):
    if BIGBUTTON.collidepoint((x, y)):
        return FUSCHIA
    return None

if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:4)

我怀疑仅当事件队列中存在多个鼠标移动事件时才会发生此错误。通常,pygame的速度足以在不产生一个新用户输入事件的情况下渲染屏幕,因此sendCoords仅在drawCircles个调用之间被调用一次。在这种情况下,coords的大小永远不会超过52。但是,如果发生多个鼠标移动事件(可能是由于系统滞后,或者因为用户非常快地摇动了鼠标),则sendCoords可能是连续叫了很多遍。因此,到drawCircles执行时,coords可以包含53个元素,甚至更多。

当您到达drawCircles时,这将成为问题:

def drawCircles(snakeLength):
    for i in range(len(coords)):
        pygame.draw.circle(SCREEN, colorList[i], coords[i], abs(i-(len(coords))) * 5, 0)#### Why does the list index go out of range?
        if i > snakeLength :   
            popList()

比方说,该函数在coords包含53个元素且snakeLength为50时执行。循环将正常迭代,直到i等于51。然后i > snakeLength将求值设为True,将调用popList。现在coords小一个元素,长度为52。循环的迭代结束,下一次迭代开始。 i等于52。pygame.draw.circle行将尝试访问coords[i],但是由于coords不再具有53个元素,coords[i]将引发IndexError尝试访问第53个元素。

Python不够聪明,无法理解如果for i in range(len(coords))的大小减小1,则coords循环应比平常提前一个迭代结束。不管列表是否会导致崩溃,它都会愉快地一直迭代到列表的原始长度。

一种可能的解决方案是将popList移出列表,因此coords的大小在迭代时不会改变。

def drawCircles(snakeLength):
    for i in range(len(coords)):
        pygame.draw.circle(SCREEN, colorList[i], coords[i], abs(i-(len(coords))) * 5, 0)#### Why does the list index go out of range?

    while len(coords) > snakeLength :   
        popList()

您可能会想:“当在for循环中不能修改coords的长度时,为什么可以在此while循环中修改它的长度呢?”关键区别在于这两个语句的评估时间。 range(len(coords))仅在循环开始前执行一次,因此不会注意到对coords的修改。但是len(coords) > snakeLength在while循环的迭代开始时执行,因此对coords的更改会立即被注意到。