在python中,是否有可能在调用后出现异常,但"before"后面的try块?
in python, is it possible for an exception to occur after a call but "before" the try block that follows it?
给定一个函数调用和紧随其后的 try 块,是否存在调用 returns 正常但引发异常但未被 try 块捕获的情况?
例如:
# example 1
resource = acquire_a_resource()
try:
resource.do_something()
# some more code...
finally:
resource.close()
有没有可能acquire_a_resource()
returns正常但resource.close()
不会被调用?
或者换句话说,是否存在以下情况:
# example 2
resource = None
try:
resource = acquire_a_resource()
resource.do_something()
# some more code...
finally:
if resource:
resource.close()
会比示例 #1 更安全吗?
可能是因为KeyboardInterrupt
/threads/signals?
是的,至少在理论上是这样,尽管在 CPython 中不是(详见脚注)。线程不是特别相关,但是您的 KeyboardInterrupt 场景恰到好处:
resource = acquire_a_resource()
调用函数。函数获取资源和returns句柄,然后在给变量赋值的过程中,1发生键盘中断。所以:
try:
不会 运行——而是发生 KeyboardInterrupt
异常,离开当前函数并取消绑定变量。
第二个版本通过 finally
子句,因此假设 if resource
找到它 boolean-truth-y,resource.close()
确实被调用。
(请注意,实际触发此操作通常非常困难:您必须恰到好处地安排中断时间。您可以增加比赛 window 很多,例如,在try
.)
在许多情况下,with
语句效果很好:
with acquire_a_resource() as resource:
resource.do_something()
其中 close
内置于 __exit__
方法中。即使块通过异常转义,方法 运行s。
1一般情况下,实现有义务完成获取资源与变量的绑定,否则会出现不可恢复的竞争。在 CPython 中,发生这种情况是因为解释器检查语句之间的中断,偶尔会在源代码中的重要位置进行检查。
CPython实际上又增加了一个特例:
/* Do periodic things. Doing this every time through
the loop would add too much overhead, so we do it
only every Nth instruction. We also do it if
``pendingcalls_to_do'' is set, i.e. when an asynchronous
event needs attention (e.g. a signal handler or
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) {
opcode = _Py_OPCODE(*next_instr);
if (opcode == SETUP_FINALLY ||
opcode == SETUP_WITH ||
opcode == BEFORE_ASYNC_WITH ||
opcode == YIELD_FROM) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
emitting a resource warning in the common idiom
'with open(path) as file:'.
- If we're about to enter the 'async with:'.
- If we're about to enter the 'try:' of a try/finally (not
*very* useful, but might help in some cases and it's
traditional)
- If we're resuming a chain of nested 'yield from' or
'await' calls, then each frame is parked with YIELD_FROM
as its next opcode. If the user hit control-C we want to
wait until we've reached the innermost frame before
running the signal handler and raising KeyboardInterrupt
(see bpo-30039).
*/
goto fast_next_opcode;
}
(Python/ceval.c
, 1000 行附近).
实际上 try
行 确实 运行,实际上,因为这里有一个 SETUP_FINALLY
。我一点也不清楚其他 Python 实现是否做同样的事情。
给定一个函数调用和紧随其后的 try 块,是否存在调用 returns 正常但引发异常但未被 try 块捕获的情况?
例如:
# example 1
resource = acquire_a_resource()
try:
resource.do_something()
# some more code...
finally:
resource.close()
有没有可能acquire_a_resource()
returns正常但resource.close()
不会被调用?
或者换句话说,是否存在以下情况:
# example 2
resource = None
try:
resource = acquire_a_resource()
resource.do_something()
# some more code...
finally:
if resource:
resource.close()
会比示例 #1 更安全吗?
可能是因为KeyboardInterrupt
/threads/signals?
是的,至少在理论上是这样,尽管在 CPython 中不是(详见脚注)。线程不是特别相关,但是您的 KeyboardInterrupt 场景恰到好处:
resource = acquire_a_resource()
调用函数。函数获取资源和returns句柄,然后在给变量赋值的过程中,1发生键盘中断。所以:
try:
不会 运行——而是发生 KeyboardInterrupt
异常,离开当前函数并取消绑定变量。
第二个版本通过 finally
子句,因此假设 if resource
找到它 boolean-truth-y,resource.close()
确实被调用。
(请注意,实际触发此操作通常非常困难:您必须恰到好处地安排中断时间。您可以增加比赛 window 很多,例如,在try
.)
在许多情况下,with
语句效果很好:
with acquire_a_resource() as resource:
resource.do_something()
其中 close
内置于 __exit__
方法中。即使块通过异常转义,方法 运行s。
1一般情况下,实现有义务完成获取资源与变量的绑定,否则会出现不可恢复的竞争。在 CPython 中,发生这种情况是因为解释器检查语句之间的中断,偶尔会在源代码中的重要位置进行检查。
CPython实际上又增加了一个特例:
/* Do periodic things. Doing this every time through
the loop would add too much overhead, so we do it
only every Nth instruction. We also do it if
``pendingcalls_to_do'' is set, i.e. when an asynchronous
event needs attention (e.g. a signal handler or
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) {
opcode = _Py_OPCODE(*next_instr);
if (opcode == SETUP_FINALLY ||
opcode == SETUP_WITH ||
opcode == BEFORE_ASYNC_WITH ||
opcode == YIELD_FROM) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
emitting a resource warning in the common idiom
'with open(path) as file:'.
- If we're about to enter the 'async with:'.
- If we're about to enter the 'try:' of a try/finally (not
*very* useful, but might help in some cases and it's
traditional)
- If we're resuming a chain of nested 'yield from' or
'await' calls, then each frame is parked with YIELD_FROM
as its next opcode. If the user hit control-C we want to
wait until we've reached the innermost frame before
running the signal handler and raising KeyboardInterrupt
(see bpo-30039).
*/
goto fast_next_opcode;
}
(Python/ceval.c
, 1000 行附近).
实际上 try
行 确实 运行,实际上,因为这里有一个 SETUP_FINALLY
。我一点也不清楚其他 Python 实现是否做同样的事情。