【问题标题】:Program Ends for No Apparent Reason (Python With Turtle)程序无故结束(Python 与 Turtle)
【发布时间】:2026-02-10 03:05:02
【问题描述】:

我的程序在一段时间后无故退出。它不会抛出任何错误,并且不满足我唯一一次调用done() 的要求。

(有时在程序退出之前满足done()的要求)。

我的代码如下:

import random
from turtle import *
import sys


sys.setrecursionlimit(10000)
l = 0
defense_x1 = -200
defense_x2 = -200
defense_x3 = -200
defense_x4 = -200
zombies = []
defenses = []
attacks = []
placement_options = [0, 1, 2, -1]

def new_zombie():
    placement_level = random.choice(placement_options)
    z = vector(200, placement_level*100)
    print(placement_level)
    zombies.append(z)

def new_defense():
  global defense_x1
  global defense_x2
  global defense_x3
  global defense_x4
  i = input("select a number 1-4 on the keyboard. ")
  if i == "1":
    c = vector(defense_x1, 200)
    goto(c.x, c.y)
    dot(20, 'green')
    defense_x1 += 21
  elif i == "2":
    c = vector(defense_x2, 100)
    goto(c.x, c.y)
    dot(20, 'green')
    defense_x2 += 21
  elif i == "3":
    c = vector(defense_x3, 0)
    goto(c.x, c.y)
    dot(20, 'green')
    defense_x3 += 21
  elif i == "4":
    c = vector(defense_x4, -100)
    goto(c.x, c.y)
    dot(20, 'green')
    defense_x4 += 21
  defenses.append(c)

def shooting():
  for defense in defenses: 
    d = vector(defense.x, defense.y)
    goto(d.x, d.y)
    dot(5, 'blue')
    attacks.append(d)

def contact():
  for z in zombies:
    for a in attacks:
      if a.x >= z.x and a.y == z.y:
        zombies.remove(z)
        attacks.remove(a)
      elif a.x > 200:
          attacks.remove(a)

def drawing():
  clear()
  for zombie in zombies:
    goto(zombie.x, zombie.y)
    dot(20, 'red')
  for defense in defenses:
    goto(defense.x, defense.y)
    dot(20, 'green')
  for attack in attacks:
    goto(attack.x, attack.y)
    dot(5, 'blue')
  update()

def movement():
  global l
  for zombie in zombies:
    zombie.x -= 0.1
  drawing()
  contact()
  for z in zombies:
    if z.x < -200:
      done()
  for a in attacks:
    if a.x > 200:
      attacks.remove(a)
    a.x += 0.5
  l += 1
  if l == 1300:
    print("Firing Weapons!")
    l = 0
    shooting()
  if random.randint(1, 350) == 2:
    new_zombie()
  if random.randint(1, 900) == 2:
    print("You can build another defense module!")
    new_defense()

  movement()

def gameplay():
  setup(420, 420, 370, 0)
  hideturtle()
  tracer(False)
  up()
  new_zombie()
  movement()

gameplay()

这里使用的代码向量如下,以防万一你想看看。

import collections.abc
import math
import os

def path(filename):
    filepath = os.path.realpath(__file__)
    dirpath = os.path.dirname(filepath)
    fullpath = os.path.join(dirpath, filename)
    return fullpath

def line(a, b, x, y):
    turtle.up()
    turtle.goto(a, b)
    turtle.down()
    turtle.goto(x, y)

class vector(collections.abc.Sequence):
    precision = 6
    __slots__ = ('_x', '_y', '_hash')

    def __init__(self, x, y):
        self._hash = None
        self._x = round(x, self.precision)
        self._y = round(y, self.precision)

    @property

    def x(self):
        return self._x

    @x.setter

    def x(self, value):
        if self._hash is not None:
            raise ValueError('Cannot set x after hashing')
        self._x = round(value, self.precision)

    @property

    def y(self):
        return self._y

    @y.setter

    def y(self, value):
        if self._hash is not None:
            raise ValueError('Cannot set y after hashing')
        self._y = round(value, self.precision)

    def __hash__(self):
        #v.__hash__() -> hash(v)
        if self._hash is None:
            pair = (self.x, self.y)
            self._hash = hash(pair)

        return self._hash

    def __len__(self):
        return 2

    def __getitem__(self, index):
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        else:
            raise IndexError

    def copy(self):
        type_self = type(self)
        return type_self(self.x, self.y)

    def __eq__(self, other):
        if isinstance(other, vector):
            return self.x == other.x and self.y == other.y
        return NotImplemented

    def __ne__(self, other):
        if isinstance(other, vector):
            return self.x != other.x and self.y != other.y
        return NotImplemented

    def __iadd__(self, other):
        if self._hash is not None:
            raise ValueError("Cannot add vector after hashing.")
        elif isinstance(other, vector):
            self.x += other.x
            self.y += other.y
        else:
            self.x += other
            self.y += other
        return self

    def __add__(self, other):
        copy = self.copy()
        return copy.__iadd__(other)

    __radd__ = __add__

    def move(self, other):
        self.__iadd__(other)

    def __isub__(self, other):
        if self._hash is not None:
            raise ValueError("Cannot subtract vector after hashing.")
        elif isinstance(other, vector):
            self.x -= other.x
            self.y -= other.y
        else:
            self.x -= other
            self.y -= other
        return self

    def __sub__(self, other):
        copy = self.copy()
        return copy.__isub__(other)

    def __imul__(self, other):
        if self._hash is not None:
            raise ValueError("Cannot multiply vector after hashing.")
        elif isinstance(other, vector):
            self.x *= other.x
            self.y *= other.y
        else:
            self.x *= other
            self.y *= other
        return self

    def __mul__(self, other):
        copy = self.copy()
        return copy.__imul__(other)

    __rmul__ = __mul__

    def scale(self, other):
        self.__imul__(other)

    def __itruediv__(self, other):
        if self._hash is not None:
            raise ValueError("Cannot divide vector after hashing.")
        elif isinstance(other, vector):
            self.x /= other.x
            self.y /= other.y
        else:
            self.x /= other
            self.y /= other
        return self

    def __truediv__(self, other):
        copy = self.copy()
        return copy.__itruediv__(other)

    def __neg__(self):
        copy = self.copy()
        copy.x = -copy.x
        copy.y = -copy.y
        return copy

    def __abs__(self):
        return (self.x**2+self.y**2)**0.5

    def rotate(self, angle):
        if self._hash is not None:
            raise ValueError("Cannot rotate vector after hashing.")
        radians= angle*math.pi/180.0
        cosine = math.cos(radians)
        sine = math.sin(radians)
        x = self.x
        y = self.y
        self.x = x*cosine - y*sine
        self.y = y*cosine + x*sine

    def __repr__(self):
        type_self = type(self)
        name = type_self.__name__
        return '{} ({!r},{!r})'.format(name, self.x, self.y)

感谢任何和所有帮助,也欢迎对我可能未正确使用的函数或代码的其他见解。感谢您的宝贵时间!

【问题讨论】:

  • 这么多代码.. 什么是“无故退出”的最小程序?除了使此类问题更加集中和适合 SO 之外,分解问题的过程(即,将相关的部分与不相关的部分隔离开来)是学习调试的一项重要技能。
  • 无论如何,由于这个程序使用 infinite 循环进行循环(请参阅movement),它会在“某个时候”出现堆栈溢出而失败。通常超过几千次迭代......不到一百万次。
  • 另外,我相信您的第二条评论可能会强调其失败的原因,因为它在数千次迭代后确实会失败。
  • 对假设的验证可能是:切换movement 以使用while True 循环而不是递归(它可能永远运行..)
  • 默认限制是 1000 并且应该很少更改 - 这只会影响 Python 的检查,它应该小于进程的最大能力,并且实际上不会增加​​进程的堆栈大小。如果还希望设置堆栈大小,请参阅 *.com/a/16248113/2864740 - 这将取决于操作系统(例如 Windows 不支持),我不建议在一般情况下进行此类更改。

标签: python exit python-turtle


【解决方案1】:

检查过,它在调用 move() 1469 次后闪退。当然,我将 input 替换为 str(random.randint(1, 4)) ,无需等待,也无需用户输入。

应该是递归调用引起的。按照操作后,它工作正常。

  • 删除函数movement()中的movement()调用
  • 将函数gameplay()中的movement()调用替换为
while True:
    movement()

【讨论】: