代码之家  ›  专栏  ›  技术社区  ›  Puna

这段代码如何执行finally块,即使它从未被评估为true?

  •  1
  • Puna  · 技术社区  · 1 年前
    def divisive_recursion(n):
        try:
            if n <= 0:
                return 1
            else:
                return n + divisive_recursion(n // divisive_recursion(n - 1))
        except ZeroDivisionError:
            return -1
        finally:
            if n == 2:
                print("Finally block executed for n=2")
            elif n == 1:
                print("Finally block executed for n=1")
    
    print(divisive_recursion(5))
    

    在这里, divisive_recursion(1) 结果在 1 + (1 // divisive_recursion(0)) 那么 divisive_recursion(0) 退货 1 它进入无限递归,其中 divisive_recursion(1) divisive_recursion(0) 被反复执行。我希望代码能够给出 RecursionError 由于这一点,它确实如此,但 finally 不知怎么的,块在那之前就被执行了,我知道它们总是被执行,除非打印出来 n 应该等于 1. 2 ,由于无限递归,它永远不会这样做,那么为什么当两个打印语句中的条件从未被计算为 True ?

    2 回复  |  直到 1 年前
        1
  •  2
  •   juanpa.arrivillaga    1 年前

    在其中一条评论中,你问“这是否意味着一旦程序遇到崩溃,它将执行所有 finally 在递归最终崩溃之前向上阻止递归”。

    答案基本上是肯定的。

    例外并不是真正的“崩溃”,或者可以把它看作是一种受控的崩溃方式。

    这里有一个简单的例子来说明,在这种情况下,异常被捕获并处理:

    >>> def foo(n):
    ...     if n == 0:
    ...         raise RuntimeError
    ...     try:
    ...         foo(n - 1)
    ...     except:
    ...         print(f'caught exception at {n=}')
    ...     finally:
    ...         print(f'in finally at {n=}')
    ... 
    >>> foo(5)
    caught exception at n=1
    in finally at n=1
    in finally at n=2
    in finally at n=3
    in finally at n=4
    in finally at n=5
    

    也许更澄清的是,这里有一个意外的例外情况:

    >>> def foo(n):
    ...     if n == 0:
    ...         raise RuntimeError
    ...     try:
    ...         foo(n - 1)
    ...     except ZeroDivisionError:
    ...         print(f'caught exception at {n=}')
    ...     finally:
    ...         print(f'in finally at {n=}')
    ... 
    >>> foo(5)
    in finally at n=1
    in finally at n=2
    in finally at n=3
    in finally at n=4
    in finally at n=5
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in foo
      File "<stdin>", line 5, in foo
      File "<stdin>", line 5, in foo
      [Previous line repeated 2 more times]
      File "<stdin>", line 3, in foo
    RuntimeError
    
        2
  •  2
  •   Mureinik    1 年前

    正确的思考方式是,每次递归调用 divisive_recursion 有自己的价值 n 它正在被调用。

    这样想:当你打电话的时候 divisive_recursion(1) ,发生了一些事情——就这个问题而言,什么并不重要,它有一个递归调用的事实也无关紧要。为了这个答案,唯一重要的是,在这次通话中,你有 n=1 无论发生什么 try 块最终提出了 RecursionError 为了争论,你本可以有一个 except: 块来更好地可视化这一点(尽管这通常不是一个很好的做法)。一旦 尝试 块终止(在这种情况下通过引发错误) finally 块被执行。自从 n 1 (在 尝试 block,所以如果函数是用 1. 它将继续存在 1. ),the elif 输入块并执行其内容。