对于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
确实在上述示例中得到了执行,但我想还有其他我没有想到的场景。
是否存在finally
块在Python中执行失败的情况?
6条答案
按热度按时间6mzjoqzu1#
是的。最后(几乎)总是胜利。
有一些方法可以在
finally:
有机会执行之前停止执行(例如,使解释器崩溃、关闭计算机、永久挂起生成器)。我想还有其他我没想到的情况。
下面还有一些你可能没有想到的:
根据您退出解释器的方式,有时您可以最终“取消”,但不是像这样:
使用不稳定的
os._exit
(在我看来,这福尔斯“崩溃解释器”):我目前正在运行这段代码,以测试最终是否仍将执行后,热寂的宇宙:
不过,我仍在等待结果,所以请稍后再回来查看。
qij5mzcb2#
根据Python documentation:
无论之前发生了什么,final-block都会在代码块完成并处理了任何引发的异常后执行,即使异常处理器或else-block中出现错误并引发了新的异常,final-block中的代码仍然会运行。
还应该注意的是,如果有多个return语句,包括finally块中的一个,那么finally块的return语句将是唯一执行的语句。
rwqw0loc3#
嗯,是也不是。
可以保证的是,Python总是会尝试执行finally块,如果你从finally块返回或者引发一个未捕获的异常,finally块会在实际返回或者引发异常之前执行。
(what您可以通过简单地运行问题中的代码来控制自己)
我能想象到的唯一一种情况是,当Python解释器本身崩溃时,例如在C代码内部或由于断电,finally块不会被执行。
llmtgqce4#
我没有使用生成器函数就找到了这个:
休眠可以是任何可能运行不一致时间量的代码。
这里发生的事情是,第一个并行进程成功地离开了try块,但随后试图从函数返回一个没有在任何地方定义的值(foo),这导致了一个异常,这个异常在其他进程到达finally块之前就杀死了map。
同样,如果你在try块中的sleep()调用之后添加
bar = bazz
行,那么第一个到达该行的进程会抛出一个异常(因为bazz没有定义),这会导致它自己的finally块运行,但随后会杀死map,导致其他try块在没有到达finally块的情况下消失,第一个进程也不会到达它的return语句。对于Python多处理来说,这意味着即使有一个进程出现异常,也不能相信异常处理机制能够清理所有进程中的资源,额外的信号处理或管理多处理Map调用之外的资源是必要的。
xdyibdwo5#
您可以将finally与if语句一起使用,下面的示例检查网络连接,如果已连接,则将运行finally块
sshcrbum6#
“保证”这个词比
finally
的任何实现都要有力得多。保证的是,如果执行流出整个try
-finally
结构,它将通过finally
这样做。不保证的是,执行将流出try
-finally
。finally
in a generator or async coroutine might never run,如果对象从未执行到结束。有很多种可能发生这种情况;这里有一个:请注意,这个例子有点棘手:当生成器被垃圾回收时,Python试图通过抛出
GeneratorExit
异常来运行finally
块,但是在这里我们捕获了那个异常,然后又捕获了yield
,此时Python打印了一个警告(“generator ignored GeneratorExit”)并放弃了。生成器或协程可能不会执行到最后的其他情况包括对象从未被GC'艾德(是的,这是可能的,即使在CPython中也是如此),或者
async with
await
在__aexit__
中,或者对象await
或yield
在finally
块中。finally
in a daemon thread might never execute(如果所有非守护进程线程都先退出)。os._exit
will halt the process immediately而不执行finally
块。os.fork
可能会导致finally
阻塞execute twice。除了发生两次的正常问题之外,如果对共享资源的访问没有正确同步,这可能会导致并发访问冲突(崩溃、停顿...)。由于
multiprocessing
在使用 fork start方法(Unix上的默认方法)时使用fork-without-exec创建工作进程,然后在工作进程完成后调用工作进程中的os._exit
,因此finally
和multiprocessing
的交互可能会有问题(example)。finally
块运行。kill -SIGKILL
将阻止finally
块运行。SIGTERM
和SIGHUP
也将阻止finally
块运行,除非您自己安装一个处理程序来控制关闭;默认情况下,Python不处理SIGTERM
或SIGHUP
。finally
中的一个异常可能会阻止清理工作的完成,一个特别值得注意的情况是,如果用户在我们开始执行finally
块时点击了control-C *,Python将引发一个KeyboardInterrupt
并跳过finally
块的每一行内容(KeyboardInterrupt
-安全代码很难写)。finally
块将不会运行。finally
块不是事务系统;它不提供原子性保证或任何类似的东西,其中一些例子可能看起来很明显,但很容易忘记这样的事情可能会发生,并过于依赖finally
。