我制作了一个程序来在Python乌龟中沿随机方向生成线条。我这样做的方式非常重复,而且也会崩溃。我的代码反复通过函数重复执行。我发现它总是在第3215次递归时崩溃。我不知道这是否相关。我在问是否有人知道它为什么崩溃以及如何停止它。崩溃时,乌龟图形窗口和cmd窗口都会随机关闭。 我的代码:
import turtle
import random
import sys
sys.setrecursionlimit(100000)
rminlength = 1
rmaxlength = 15
gminlength = 1
gmaxlength = 30
bminlength = 1
bmaxlength = 45
rminangle = 45
rmaxangle = 45
gminangle = 90
gmaxangle = 90
bminangle = 135
bmaxangle = 135
drawspeed = 10000
global recurse
recurse = 0
r = turtle.Turtle()
r.color('red')
r.pensize(3)
r.shape('circle')
r.speed(drawspeed)
r.hideturtle()
g = turtle.Turtle()
g.color('green')
g.pensize(3)
g.shape('circle')
g.speed(drawspeed)
g.hideturtle()
b = turtle.Turtle()
b.color('blue')
b.pensize(3)
b.shape('circle')
b.speed(drawspeed)
b.hideturtle()
#Movement
def rmove():
if(random.randint(1,2) == 1):
r.left(random.randint(rminangle,rmaxangle))
if(random.randint(1,2) == 1):
r.forward(random.randint(rminlength,rmaxlength))
else:
r.backward(random.randint(rminlength,rmaxlength))
else:
r.right(random.randint(rminangle,rmaxangle))
if(random.randint(1,2) == 1):
r.forward(random.randint(rminlength,rmaxlength))
else:
r.backward(random.randint(rminlength,rmaxlength))
global recurse
recurse+=1
print(recurse)
gmove()
def gmove():
if(random.randint(1,2) == 1):
g.left(random.randint(gminangle,gmaxangle))
if(random.randint(1,2) == 1):
g.forward(random.randint(gminlength,gmaxlength))
else:
g.backward(random.randint(gminlength,gmaxlength))
else:
g.right(random.randint(gminangle,gmaxangle))
if(random.randint(1,2) == 1):
g.forward(random.randint(gminlength,gmaxlength))
else:
g.backward(random.randint(gminlength,gmaxlength))
global recurse
recurse+=1
print(recurse)
bmove()
def bmove():
if(random.randint(1,2) == 1):
b.left(random.randint(bminangle,bmaxangle))
if(random.randint(1,2) == 1):
b.forward(random.randint(bminlength,bmaxlength))
else:
b.backward(random.randint(bminlength,bmaxlength))
else:
b.right(random.randint(bminangle,bmaxangle))
if(random.randint(1,2) == 1):
b.forward(random.randint(bminlength,bmaxlength))
else:
b.backward(random.randint(bminlength,bmaxlength))
global recurse
recurse+=1
print(recurse)
rmove()
rmove()
input('Crashed')
答案 0 :(得分:2)
每当调用一个函数时,就会在分配给名为stack的程序的有限数据区域中使用内存。如果这些函数从未解析并继续调用其他函数,则将永远不会回收堆栈存储器。最终,程序将耗尽内存。这称为stack overflow。
这是程序中的实际错误消息:
...
File "a.py", line 100, in bmove
rmove()
File "a.py", line 64, in rmove
gmove()
File "a.py", line 82, in gmove
bmove()
File "a.py", line 99, in bmove
print(recurse)
MemoryError: stack overflow
您试图通过增加Python设置的递归限制来解决该问题,但这只会推迟不可避免的事情。即使某些调用确实可以解决,增加此数目也是一种不安全的编写代码的方法,因为它会假设堆栈大小,而不是重写程序逻辑来确保调用能够解决并且堆栈不会失控。
由于不需要递归来获得要移动的乌龟,所以让我们重新编写程序以使用循环而不是使用函数调用来控制乌龟:
import turtle
import random
import sys
rminlength = 1
rmaxlength = 15
gminlength = 1
gmaxlength = 30
bminlength = 1
bmaxlength = 45
rminangle = 45
rmaxangle = 45
gminangle = 90
gmaxangle = 90
bminangle = 135
bmaxangle = 135
drawspeed = 10000
r = turtle.Turtle()
r.color('red')
r.pensize(3)
r.shape('circle')
r.speed(drawspeed)
r.hideturtle()
g = turtle.Turtle()
g.color('green')
g.pensize(3)
g.shape('circle')
g.speed(drawspeed)
g.hideturtle()
b = turtle.Turtle()
b.color('blue')
b.pensize(3)
b.shape('circle')
b.speed(drawspeed)
b.hideturtle()
#Movement
def rmove():
if(random.randint(1,2) == 1):
r.left(random.randint(rminangle,rmaxangle))
if(random.randint(1,2) == 1):
r.forward(random.randint(rminlength,rmaxlength))
else:
r.backward(random.randint(rminlength,rmaxlength))
else:
r.right(random.randint(rminangle,rmaxangle))
if(random.randint(1,2) == 1):
r.forward(random.randint(rminlength,rmaxlength))
else:
r.backward(random.randint(rminlength,rmaxlength))
def gmove():
if(random.randint(1,2) == 1):
g.left(random.randint(gminangle,gmaxangle))
if(random.randint(1,2) == 1):
g.forward(random.randint(gminlength,gmaxlength))
else:
g.backward(random.randint(gminlength,gmaxlength))
else:
g.right(random.randint(gminangle,gmaxangle))
if(random.randint(1,2) == 1):
g.forward(random.randint(gminlength,gmaxlength))
else:
g.backward(random.randint(gminlength,gmaxlength))
def bmove():
if(random.randint(1,2) == 1):
b.left(random.randint(bminangle,bmaxangle))
if(random.randint(1,2) == 1):
b.forward(random.randint(bminlength,bmaxlength))
else:
b.backward(random.randint(bminlength,bmaxlength))
else:
b.right(random.randint(bminangle,bmaxangle))
if(random.randint(1,2) == 1):
b.forward(random.randint(bminlength,bmaxlength))
else:
b.backward(random.randint(bminlength,bmaxlength))
while 1: # loop infinitely
rmove()
gmove()
bmove()
具体来说,已删除了递归调用,并添加了while 1:
无限循环。
正如您提到的,代码中有很多重复。编写一个类来封装乌龟逻辑可提供大量的清理机会,并使该程序易于扩展以处理任意数量的其他乌龟:
import turtle
from random import choice
from random import randint
class Turtle:
def __init__(
self, color, min_len, max_len, angle, speed=10, pensize=3
):
self.min_len = min_len
self.max_len = max_len
self.angle = angle
self.turt = turtle.Turtle()
self.turt.color(color)
self.turt.pensize(pensize)
self.turt.speed(speed)
self.turt.hideturtle()
def move(self):
choice((self.turt.left, self.turt.right))(self.angle)
dir_func = choice((self.turt.forward, self.turt.backward))
dir_func(randint(self.min_len, self.max_len))
if __name__ == "__main__":
turtles = [
Turtle("red", 1, 15, 45),
Turtle("green", 1, 30, 90),
Turtle("blue", 1, 45, 135)
]
while 1:
for turt in turtles:
turt.move()
快乐的旅行!
答案 1 :(得分:1)
在我的系统上,您的代码超过了4000次调用而没有崩溃。关键是,尽管在setrecursionlimit()
的各种设置下,每个系统崩溃的位置都会有所不同,但最终会崩溃。
由于您没有使用递归的任何优势(在递归之间没有学到任何东西或在递归之间没有传递任何东西),而是要模拟协程,因此,请使用生成器来模拟它们而无需递归:
from turtle import Screen, Turtle
from random import randint, choice
R_MIN_LENGTH, R_MAX_LENGTH = 1, 15
G_MIN_LENGTH, G_MAX_LENGTH = 1, 30
B_MIN_LENGTH, B_MAX_LENGTH = 1, 45
R_MIN_ANGLE, R_MAX_ANGLE = 45, 45
G_MIN_ANGLE, G_MAX_ANGLE = 90, 90
B_MIN_ANGLE, B_MAX_ANGLE = 135, 135
R_LIMIT = 1500
G_LIMIT = 1250
B_LIMIT = 1000
DRAW_SPEED = 'fastest'
# Movement
def rmove(turtle):
count = 0
while count < R_LIMIT:
if choice([True, False]):
turtle.left(randint(R_MIN_ANGLE, R_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(R_MIN_LENGTH, R_MAX_LENGTH))
else:
turtle.backward(randint(R_MIN_LENGTH, R_MAX_LENGTH))
else:
turtle.right(randint(R_MIN_ANGLE, R_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(R_MIN_LENGTH, R_MAX_LENGTH))
else:
turtle.backward(randint(R_MIN_LENGTH, R_MAX_LENGTH))
count += 1
yield count
def gmove(turtle):
count = 0
while count < G_LIMIT:
if choice([True, False]):
turtle.left(randint(G_MIN_ANGLE, G_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(G_MIN_LENGTH, G_MAX_LENGTH))
else:
turtle.backward(randint(G_MIN_LENGTH, G_MAX_LENGTH))
else:
turtle.right(randint(G_MIN_ANGLE, G_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(G_MIN_LENGTH, G_MAX_LENGTH))
else:
turtle.backward(randint(G_MIN_LENGTH, G_MAX_LENGTH))
count += 1
yield count
def bmove(turtle):
count = 0
while count < B_LIMIT:
if choice([True, False]):
turtle.left(randint(B_MIN_ANGLE, B_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(B_MIN_LENGTH, B_MAX_LENGTH))
else:
turtle.backward(randint(B_MIN_LENGTH, B_MAX_LENGTH))
else:
turtle.right(randint(B_MIN_ANGLE, B_MAX_ANGLE))
if choice([True, False]):
turtle.forward(randint(B_MIN_LENGTH, B_MAX_LENGTH))
else:
turtle.backward(randint(B_MIN_LENGTH, B_MAX_LENGTH))
count += 1
yield count
r = Turtle('circle', visible=False)
r.color('red')
r.pensize(3)
r.speed(DRAW_SPEED)
red = rmove(r)
g = Turtle('circle', visible=False)
g.color('green')
g.pensize(3)
g.speed(DRAW_SPEED)
green = gmove(g)
b = Turtle('circle', visible=False)
b.color('blue')
b.pensize(3)
b.speed(DRAW_SPEED)
blue = bmove(b)
# written this way so each turtle can have it's own independent limit, as desired
while next(red, R_LIMIT) + next(green, G_LIMIT) + next(blue, B_LIMIT) < R_LIMIT + G_LIMIT + B_LIMIT:
pass
screen = Screen()
screen.exitonclick()