繁体   English   中英

在python中,是否可能在调用之后但紧随其后的try块之前发生异常?

[英]in python, is it possible for an exception to occur after a call but “before” the try block that follows it?

给定一个函数调用和一个紧随其后的try块,是否存在调用正常返回但引发了异常且没有被try块捕获的情况?

例如:

# example 1
resource = acquire_a_resource()
try:
    resource.do_something()
    # some more code...
finally:
    resource.close()

是否有可能acquire_a_resource()正常返回但不会调用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()

调用函数。 该函数获取资源并返回该句柄,然后在分配给变量1的过程中发生键盘中断。 所以:

try:

不会运行-而是发生KeyboardInterrupt异常,保留当前函数并取消绑定变量。

第二个版本通过finally子句,因此假设if resource发现它为boolean-truth-y,则将调用resource.close()

(请注意,实际上触发此操作通常非常困难:您必须恰好计时中断。您可以通过例如在try之前添加time.sleep(1)来大大增加竞赛窗口。)

在许多情况下, with语句可以很好地工作:

with acquire_a_resource() as resource:
    resource.do_something()

close内置在__exit__方法中。 即使通过异常转义了该块,该方法也会运行。


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实现是否也做同样的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM