【问题标题】:Why does this Python code behave so weirdly?为什么这段 Python 代码的行为如此怪异?
【发布时间】:2022-10-15 02:35:26
【问题描述】:

首先我尝试了这个

def x():
   try:
      1/0 # just an division error to get an exception
   except:
      x()

这段代码在 3.10 中表现正常,我得到了RecursionError: maximum recursion depth exceeded,正如我所料,但 3.8 进入堆栈溢出并且没有正确处理递归错误。但我确实记得在旧版本的 Python 中也有 RecursionError,所以我尝试了

def x(): x()

这在两个版本的 Python 中都返回了RecursionError

就好像(在第一个 sn-p 中)递归错误永远不会在 except 中抛出,而是在调用的函数中抛出,然后在调用但由 try-except 处理的函数的第一条指令处抛出错误。

然后我尝试了别的东西:

def x():
   try:
      x()
   except:
      x()

这在某种程度上更奇怪,堆栈溢出低于 3.10 但它卡在 3.10 的循环中

你能解释一下这种行为吗?

更新 @MisterMiyagi 发现了一个更奇怪的行为,在 <=python3.9 的 except 中添加语句不会导致 stackoverflow

def x():
   try:
      1/0
   except:
      print("")
      x()

【问题讨论】:

  • 在最后一种情况下,它会在处理错误时弹出顶部堆栈帧,以便为另一个递归提供空间。因此它将永远在最后 2 个递归级别之间循环。
  • 是的,但这需要很长时间,特别是 2 ** sys.getrecursionlimit()。
  • Specifically, there will be a total of 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376 function calls with the default recursion limit of 1000.
  • 尝试将递归限制设置得更低(类似于 5),然后将一些打印语句添加到 x 看看会发生什么
  • @MisterMiyagi 与 sys.setrecursionlimit(10) 的结果相同

标签: python recursion stack-overflow try-except pythoninterpreter


【解决方案1】:

你的异常太宽泛了,你应该像这样指定你捕获的异常类型:

def x():
    try:
        1 / 0  # just an division error to get an exception
    except ZeroDivisionError:
        x()

try:
    x()
except RecursionError:
    print("You got an recursion error")

在您的代码中,全部异常被捕获,还有 RecursionErrors。我的猜测是,这可能导致递归错误也进入第二个分支,继续调用你的 x 函数。显然在 3.10 中这是固定的。

一般来说,建议是总是包括您要捕获的错误类型。你的第二个版本卡住是因为 x 总是被调用,有和没有递归错误,所以你进入一个无限循环。

如果您想同时捕获 ZeroDivisionError 和 RecursionError,则必须在函数调用周围放置 try/except 以解决递归错误

【讨论】:

  • 我不明白这些是如何适用的。在第一种情况下,会发生递归错误过于宽泛的处理。在第二种情况下,真正的无限路径也在处理之外——那么它为什么与没有事先处理的情况不同呢?这个答案基本上似乎假设错误处理程序在 3.10 之前完全被破坏了。
  • 是的,但这不是生产代码,我的意思是抓住它们。
  • 不,如果没有指定例外,我会预计还有递归错误被捕获。这就是为什么不允许在没有异常类型错误的情况下使用“除外”的原因。我更惊讶的是,在以后的 python 版本中,递归错误无论如何都会被抛出
  • 这就是代码的作用,显式添加它们不会改变结果。
  • 正如我所展示的,递归错误的 try/except 应该围绕函数调用进行。否则,内部会抛出异常,异常
【解决方案2】:

3.10 和其他版本的不同行为似乎是由于 Python 问题 (python/cpython#86666),您还可以在 Python 2.7 上看到正确的错误。

打印“修复”了一些事情,因为它让 Python 再次检查递归限制,并且通过一条可能没有损坏的路径。你可以看到它在哪里执行here 的代码,它还会跳过重复检查对象是否支持 Vectorcall 调用协议,所以像 int 这样的东西会保留致命错误。

【讨论】:

    猜你喜欢
    • 2013-06-26
    • 2012-12-24
    • 1970-01-01
    • 2023-03-05
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    相关资源
    最近更新 更多