Ответ 1
"Гарантированный" - это гораздо более сильное слово, чем любая реализация, которую, finally
заслуживает. Гарантируется, что, если выполнение вытекает из всей try
- finally
построит, оно пройдет через finally
чтобы сделать это. То, что не гарантируется, заключается в том, что выполнение будет вытекать из try
- finally
.
-
finally
в генераторе или асинхронной сопрограмме никогда не будет работать, если объект никогда не завершится. Есть много способов, которые могут произойти; здесь один:def gen(text): try: for line in text: try: yield int(line) except: # Ignore blank lines - but catch too much! pass finally: print('Doing important cleanup') text = ['1', '', '2', '', '3'] if any(n > 1 for n in gen(text)): print('Found a number') print('Oops, no cleanup.')
Обратите внимание, что этот пример немного сложнее: когда генератор собирает мусор, Python пытается запустить блок
finally
, бросая исключениеGeneratorExit
, но здесь мы поймаем это исключение, а затем сноваyield
, и в этот момент Python выводит предупреждение (" генератор игнорируется GeneratorExit ") и отказывается. Подробнее см. PEP 342 (Coroutines через Enhanced Generators).Другие способы генератор или сопрограммный не может Выполнит на заключение включать, если объект не только никогда не GC'ed (да, возможно, даже в CPython), или если
async with
await
в__aexit__
, или если объектawait
илиyield
вfinally
блокировать. Этот список не является исчерпывающим. -
finally
в потоке демона никогда не будет выполняться, если все не-демонные потоки выйдут первым. -
os._exit
остановит процесс, не выполняяfinally
блоки. -
os.fork
может привести к тому, что блокиfinally
будут выполняться дважды. Как и обычные проблемы, которые вы ожидаете от событий, происходящих в два раза, это может привести к конфликтам одновременного доступа (сбоям, остановкам...), если доступ к общим ресурсам неправильно синхронизирован.Поскольку
multiprocessing
используют вилы-без-Exec для создания рабочих процессов при использовании метода запуска вилки (по умолчанию на Unix), а затем вызываетos._exit
в рабочем, как только работа работника не будет сделана, вfinally
иmultiprocessing
взаимодействие может быть проблематичным (пример). - Ошибка сегментации C-уровня позволит предотвратить
finally
блоки от бега. -
kill -SIGKILL
предотвратитfinally
блоки от бега.SIGTERM
иSIGHUP
также предотвратитьfinally
блоки от запуска, если не установить обработчик для управления отключающей себя; по умолчанию Python не обрабатываетSIGTERM
илиSIGHUP
. - Исключение в
finally
может предотвратить завершение очистки. Особенно примечательным является случай, когда пользователь нажимает на control-C так же, как мы начинаем выполнять блокfinally
. Python подниметKeyboardInterrupt
и пропустит каждую строку содержимого блокаfinally
. (KeyboardInterrupt
-safe код очень сложно записать). - Если компьютер теряет питание, или если он находится в спящем режиме и не просыпается,
finally
блоки не будут работать.
Блок finally
не является транзакционной системой; он не обеспечивает гарантии атомарности или что-то в этом роде. Некоторые из этих примеров могут показаться очевидными, но легко забыть, что такие вещи могут произойти, и в finally
полагаться на слишком много.