Да, по крайней мере, в теории, но не в 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 то же самое.