“终于”总是用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
,所以finally
和multiprocessing
交互可能会产生问题(例如)。
finally
阻止块运行。 kill -SIGKILL
将阻止finally
块运行。 SIGTERM
和SIGHUP
也会阻止finally
块运行,除非你自己安装一个处理程序来控制关闭; 默认情况下,Python不处理SIGTERM
或SIGHUP
。 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?