简体   繁体   English

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

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

Given a function call and a try block that immediately follows it, is there any scenario where the call returns normally but an exception is raised and not caught by the try block? 给定一个函数调用和一个紧随其后的try块,是否存在调用正常返回但引发了异常且没有被try块捕获的情况?

For example: 例如:

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

Is it possible that acquire_a_resource() returns normally but resource.close() will not be called? 是否有可能acquire_a_resource()正常返回但不会调用resource.close()

Or in other words, is there any scenario where: 换句话说,是否存在以下情况:

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

would be safer than example #1? 会比示例1更安全吗?

Maybe because of something to do with KeyboardInterrupt /threads/signals? 也许是因为与KeyboardInterrupt / threads / signals有关?

Yes, at least in theory, though not in CPython (see footnote for details). 是的,至少在理论上是这样,尽管在CPython中不是(有关详细信息,请参见脚注)。 Threading is not particularly relevant, but your KeyboardInterrupt scenario is just right: 线程并不是特别相关,但是您的KeyboardInterrupt场景恰好是正确的:

resource = acquire_a_resource()

calls the function. 调用函数。 The function acquires the resource and returns the handle, and then during the assignment to the variable, 1 the keyboard interrupt occurs. 该函数获取资源并返回该句柄,然后在分配给变量1的过程中发生键盘中断。 So: 所以:

try:

does not run—the KeyboardInterrupt exception happens instead, leaving the current function and unbinding the variable. 不会运行-而是发生KeyboardInterrupt异常,保留当前函数并取消绑定变量。

The second version passes through the finally clause, so assuming if resource finds it boolean-truth-y, resource.close() does get called. 第二个版本通过finally子句,因此假设if resource发现它为boolean-truth-y,则将调用resource.close()

(Note that actually triggering this is often very difficult: you have to time the interrupt just right. You can increase the race window a lot by, eg, adding a time.sleep(1) before the try .) (请注意,实际上触发此操作通常非常困难:您必须恰好计时中断。您可以通过例如在try之前添加time.sleep(1)来大大增加竞赛窗口。)

For many cases, a with statement works well: 在许多情况下, with语句可以很好地工作:

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

where the close is built into the __exit__ method. close内置在__exit__方法中。 The method runs even if the block is escaped via exception. 即使通过异常转义了该块,该方法也会运行。


1 In general, the implementation is obligated to complete the binding of the acquired resource to the variable, otherwise there's an irrecoverable race. 1通常,实现必须完成所获取资源与变量的绑定,否则将发生不可恢复的竞争。 In CPython this happens because the interpreter checks for interrupts between statements, and occasionally in strategic places in the source. 在CPython中,发生这种情况的原因是解释器检查语句之间的中断,并偶尔检查源代码中的重要位置。

CPython actually adds another special case: 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 , near line 1000). Python/ceval.c ,在1000行附近)。

So actually the try line does run, in effect, because there's a SETUP_FINALLY here. 实际上, try确实有效,因为这里有一个SETUP_FINALLY It's not at all clear to me whether other Python implementations do the same thing. 我还不清楚其他Python实现是否也做同样的事情。

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

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