调用 Python 对象时超过最大递归深度

2 投票
3 回答
8464 浏览
提问于 2025-04-18 01:59

我的目标是创建一个叫做箭头的类,这样我就可以有多个箭头,而不仅仅是一个。我想从坐标200, 200开始,并希望每100毫秒把x坐标增加15。但是当我尝试运行这段代码时,出现了以下错误:

  File "game.py", line 25, in moveArrow
    self.after(100, self.moveArrow(arrow, xCoord+15, yCoord)) #repeat, changing x
  File "game.py", line 24, in moveArrow
    arrow.place(x = xCoord, y = yCoord) #replace with new x,y
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1860, in place_configure
    + self._options(cnf, kw))
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1055, in _options
    elif isinstance(v, (tuple, list)):
RuntimeError: maximum recursion depth exceeded while calling a Python object

错误信息显示:“在文件 'game.py' 的第25行,调用 move Arrow 的时候,self.after(100, self.moveArrow(arrow, xCoord+15, yCoord)) #重复,改变x”这个错误信息出现得非常频繁。”

from Tkinter import *
from random import randint
from PIL import ImageTk, Image

class App(Frame):
        def __init__(self, master=None):
                Frame.__init__(self, master, height=400, width=400)
                self.master = master
                self.master.bind('<Shift_L>', self.createArrow)
        def createArrow(self, event):
                self.arrow = Arrow(self)
                self.arrow.moveArrow(self.arrow, 200, 200)

class Arrow(Frame):
        def __init__(self, master):
                Frame.__init__(self, master)
                self.arrowImage = ImageTk.PhotoImage(Image.open("arrow.gif"))
                Label(self, image=self.arrowImage).pack()
        def moveArrow(self, arrow, xCoord, yCoord):
                arrow.place_forget()
                arrow.place(x = xCoord, y = yCoord)
                self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))

root = Tk()
root.title("Mein erstes Spiel")
app = App(master=root).pack()
root.mainloop()

3 个回答

0

出问题的那行代码是:

self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))x

因为 moveArrow 这个函数没有结束条件。每当我们在编程中使用递归时,都需要一个结束条件,这样才能知道什么时候不再继续调用同一个函数。

举个例子:判断一个数是否是质数的递归方法。

int isPrime(int num,int i){

    if(i==1){
        return 1;
    }else{
       if(num%i==0)
         return 0;
       else
         isPrime(num,i-1);
    }
}

在上面的代码中,结束条件是 if (i==1)if(num%i==0)。在这两个条件下,程序不会再调用 isPrime 这个函数,递归就会在这里结束。

请添加一个结束条件,然后再运行一次。

1

你在 moveArrow() 方法里调用了 self.moveArrow(arrow, xCoord+15, yCoord)

这样就会导致无限循环,因为没有任何地方可以停止这个循环。

如果你想了解如何在 Python 中创建递归方法,可以看看 这里

如果你想实现一些简单的移动效果,可以用一个循环来做。比如说,让箭头在 x 轴上移动 200 像素,y 轴上也移动 200 像素,生成一个简单的 for 循环,并在移动箭头时加上延迟。

伪代码示例:

def moveArrow(....)
    loop:
        x += 10
        y += 10
        change_arrow_place(...)
10

其他回答都说得对,问题的源头就是这一行代码:

self.after(100, self.moveArrow(arrow, xCoord+15, yCoord))

不过,这个答案是针对Tkinter的:

你可以查看一下after方法的文档,了解如何正确使用这个方法。如果你像普通函数那样调用它,程序就会进入无限循环,一直停留在这个函数调用上。使用after时,你有两种选择:

第一种是传入时间参数,然后是回调函数,最后是回调函数的参数:

self.after(100, self.moveArrow, arrow, xCoord+15, yCoord)

第二种是使用lambda表达式来保存函数调用:

self.after(100, lambda: self.moveArrow(arrow, xCoord+15, yCoord))

撰写回答