“终于”总是用Python执行吗?

对于Python中任何可能的try-finally块,它是否可以保证finally块总是会被执行?

例如,假设我在一个except块中返回:

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

或者,也许我重新提出一个Exception

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

个人测试表明, finally会为上述示例执行,但我想象还有其他场景我没有想到。

有没有在Python中可能无法执行finally块的场景?


“保证”比任何finally应得的实现都要强大得多。 有保证的是,如果执行流出整个try - finally构造,它将通过finally来完成。 不能保证的是,执行将流出try - finally

  • 如果对象从不执行到结论,那么finally在生成器或异步协程中可能永远不会运行。 有很多方法可能发生; 这里是一个:

    def gen(text):
        try:
            for line in text:
                try:
                    yield int(line)
                except:
                    # Ignore blank lines - but catch too much!
                    pass
        finally:
            print('Doing important cleanup')
    
    text = ['1', '', '2', '', '3']
    
    if any(n > 1 for n in gen(text)):
        print('Found a number')
    
    print('Oops, no cleanup.')
    

    请注意,这个例子有点棘手:当生成器被垃圾收集时,Python试图通过抛出一个GeneratorExit异常来运行finally块,但是在这里我们捕获该异常,然后再次yield ,此时Python会打印一条警告(“发电机忽略GeneratorExit“)并放弃。 有关详细信息,请参阅PEP 342(通过增强型发生器的协同程序)。

    发电机或协同程序可能不会执行到结束的其他方式包括:如果对象只是从来没有GC'ed(是的,这是可能的,即使在CPython的),或者如果async with await S IN __aexit__ ,或者如果对象await S或yield s在finally一块。 这份清单并不是详尽无遗的。

  • 如果所有非守护进程线程先退出,那么守护线程中的finally可能永远不会执行。

  • os._exit会立即停止进程而不执行finally块。

  • os.fork可能会导致finally块执行两次。 除了您所期望的事情发生两次的正常问题外,如果对共享资源的访问未正确同步,则可能会导致并发访问冲突(崩溃,失速等)。

    由于multiprocessing使用fork-without-exec在使用fork start方法(Unix上的默认方法)时创建工作进程,并且在worker的作业完成后调用os._exit中的os._exit ,所以finallymultiprocessing交互可能会产生问题(例如)。

  • C级分段错误将finally阻止块运行。
  • kill -SIGKILL将阻止finally块运行。 SIGTERMSIGHUP也会阻止finally块运行,除非你自己安装一个处理程序来控制关闭; 默认情况下,Python不处理SIGTERMSIGHUP
  • finally一个例外可以防止清理完成。 一个特别值得注意的情况是,如果用户点击control-C,就像我们开始执行finally块一样。 Python会引发一个KeyboardInterrupt并跳过finally块内容的每一行。 ( KeyboardInterrupt代码很难写)。
  • 如果计算机失去电源,或者如果它休眠并且不唤醒, finally块将不会运行。
  • finally块不是交易系统; 它不提供原子性保证或任何类型的东西。 其中一些例子看起来很明显,但很容易忘记这种事情可能发生并finally依赖于太多。


    是。 最后总是赢。

    打败它的唯一方法是在finally:终止执行之前暂停执行finally:获得执行的机会(例如,解释器崩溃,关闭计算机,永久挂起生成器)。

    我想象还有其他场景我没有想到。

    这里有一些你可能没有想过的东西:

    def foo():
        # finally always wins
        try:
            return 1
        finally:
            return 2
    
    def bar():
        # even if he has to eat an unhandled exception, finally wins
        try:
            raise Exception('boom')
        finally:
            return 'no boom'
    

    根据你如何退出口译员,有时你可以最终“取消”,但不是这样:

    >>> import sys
    >>> try:
    ...     sys.exit()
    ... finally:
    ...     print('finally wins!')
    ... 
    finally wins!
    $
    

    使用不稳定的os._exit (在我看来,这属于“崩溃解释器”):

    >>> import os
    >>> try:
    ...     os._exit(1)
    ... finally:
    ...     print('finally!')
    ... 
    $
    

    我目前正在运行该代码,以测试在宇宙热量死亡后最终是否仍然执行:

    try:
        while True:
           sleep(1)
    finally:
        print('done')
    

    不过,我仍然在等待结果,所以稍后再回来看看。


    根据Python文档:

    无论先前发生了什么,一旦完成代码块并处理任何引发的异常,就会执行最终块。 即使异常处理程序或else块中有错误,并且引发了新的异常,最终块中的代码仍然运行。

    还应该注意的是,如果有多个返回语句,包括finally块中的一个,那么finally块返回是唯一将要执行的语句。

    链接地址: http://www.djcxy.com/p/12803.html

    上一篇: Does 'finally' always execute in Python?

    下一篇: Why is try {...} finally {...} good; try {...} catch{} bad?