【问题标题】:Trouble with try and except blocks with recursion递归的 try 和 except 块的问题
【发布时间】:2021-04-04 16:19:36
【问题描述】:

我无法理解以下内容:

import sys

def f(n):
    try:
        print('try', n)
        f(n + 1)
    except:
        print('except', n)
        if n > 0:
            raise

sys.setrecursionlimit(1000)
f(0)

它的输出是:

try 0
try 1
try 2
...
try 995
except 996
except 995
...
except 0

我知道try 块将迭代并打印n,直到满足系统递归限制,在这种情况下输入 except 块。我的问题是,在except 块中,为什么n 会减少,这里的递归发生在哪里?此外,为什么n 不能精确地满足递归限制?也就是为什么序列达不到try 1000?谢谢!

【问题讨论】:

    标签: python recursion


    【解决方案1】:

    当你在最深的调用中得到异常时,你会转到异常部分并打印,这很容易理解,对吧?然后你再次引发你得到的相同异常,现在是前一个得到异常的调用者然后转到 except 块并打印,因为它是前一个调用者,它将具有以前的 n 值,因此少打印一个,冲洗重复,你只是走回你的递归路径

    >>> sys.setrecursionlimit(5)
    >>> f(0)
    try 0
    try 1
    try 2
    try 3
    try 4
    try 5
    try 6
    try 7
    try 8
    try 9
    try 10
    try 11
    try 12
    try 13
    try 14
    try 15
    try 16
    except 16
    except 15
    except 14
    except 13
    except 12
    except 11
    except 10
    except 9
    except 8
    except 7
    except 6
    except 5
    except 4
    except 3
    except 2
    except 1
    except 0
    >>> 
    

    至于为什么 n 没有达到您设置的确切值,我不确定这是我不熟悉的 python 内部的东西,在我的情况下它超过了 11

    【讨论】:

      【解决方案2】:

      您问题的第二部分“为什么序列没有达到尝试 1000”实际上是一个非常有趣的问题,但我将从您的第一个问题(“为什么n 减少)开始,因为它更简单回答。

      n 的值减小,因为当您达到最大递归限制时,您的调用堆栈开始解散。当一个函数执行完毕后,它会返回给调用它的人。把它想象成在你的盘子里加了 10 个煎饼,然后从上到下一个接一个地吃。你吃的第一个将是顶部的煎饼(最后一个函数调用),你吃的最后一个将在底部(第一个函数调用,即f(0))。所以n 在你递归调用函数时增加,然后减少它开始解开回到原来的调用者(即main)。

      它没有准确到达1000 的原因有点有趣。如果我尝试下面的代码,我们会得到答案,除了999

      def f(n):
          n += 1
          try:
              f(n)
          except RecursionError:
              print(n)
      f(0)
      >>> 999
      

      答案是999 而不是1000,因为堆栈开始时 main 已经压入它上面。现在如果我们在递归之前添加一个打印语句,答案就变成了996

      def f(n):
          n += 1
          try:
              print(n)
              f(n)
          except RecursionError:
              print(n)
      f(0)
      >>> 1
      >>> 2
      >>> ...
      >>> 996
      

      我怀疑如果你查看 print 语句的底层实现,它有一些额外的嵌套函数调用。这意味着f(n) 调用不会触发递归错误,但打印语句会触发,这就是为什么我们得到996 而不是999(即打印语句的深度为3)。

      【讨论】:

        【解决方案3】:
        1. Why Python raises RecursionError before it exceeds the real recursion limit? 先前已回答您关于为什么没有达到递归限制的第一个问题,说明有:
        1. 关于您的第二个问题,即为什么例外数字在减少:
        • 您的代码会创建一个调用堆栈:

          f(0)
            f(1)
              ...
              f(997)  # Exception is raised when you get to the stack limit
          

        代码行:

        except:
            print('except', n)
                if n > 0:
                    raise
        

        异常被报告给父级并被父级捕获,除了块: 打印 n 的值 重新引发异常(即 raise 语句),将其传递回其父级

        所以我们得到异常序列:

             f(996)  # receives except from f(1+996), prints 996, and raises exception to its parent
           f(995)  # receives exception from f(1+995) call, print 995, and raises exception to its parent
          ...
          f(1)  # receives exception from f(1+1) call, prints 1, and raises except to its aprent
         f(0) # receives exception from f(1+0) call and prints 0
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-20
          • 2016-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-02
          • 2021-10-26
          相关资源
          最近更新 更多