в python возможно ли возникновение исключения после вызова, но "до" блока try, следующего за ним? - PullRequest
1 голос
/ 08 мая 2019

Учитывая вызов функции и блок 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 / сигналов?

1 Ответ

4 голосов
/ 08 мая 2019

Да, по крайней мере, в теории, но не в CPython (подробности см. В сноске).Потоки не особенно актуальны, но ваш сценарий KeyboardInterrupt прав:

resource = acquire_a_resource()

вызывает функцию.Функция получает ресурс и возвращает дескриптор, а затем во время присвоения переменной 1 происходит прерывание клавиатуры.Итак:

try:

не запускается - вместо этого возникает исключение KeyboardInterrupt, оставляя текущую функцию и отменяя привязку переменной.

Вторая версия проходит через предложение finally, поэтомуесли предположить, что if resource найдет его логическим-правда-у, resource.close() будет вызван.

(Обратите внимание, что на самом деле вызвать это часто очень сложно: вам нужно правильно рассчитать время прерывания. Вы можете увеличить гонку.много, например, добавив time.sleep(1) перед try.)

Во многих случаях хорошо работает оператор 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 то же самое.

...