Гарантируется ли, что для любого возможного блока try-finally в Python finally
всегда будет выполняться блок?
Например, скажем, я возвращаюсь, находясь в except
блоке:
try:
1/0
except ZeroDivisionError:
return
finally:
print("Does this code run?")
Или, может быть, я повторно подниму Exception
:
try:
1/0
except ZeroDivisionError:
raise
finally:
print("What about this code?")
Тестирование показывает, что это finally
действительно выполняется для приведенных выше примеров, но я полагаю, что есть другие сценарии, о которых я не думал.
Существуют ли какие-либо сценарии, при которых finally
блок может не выполняться в Python?
python
exception-handling
try-catch-finally
finally
Stevoisiak
источник
источник
finally
не выполнить или «нарушить его цель» - это бесконечный циклsys.exit
или принудительное прерывание. В документации указано, чтоfinally
это всегда выполняется, поэтому я бы согласился с этим.finally
завершите процесс, он не запустится. Или то же самое, если компьютер вылетает раньше: Dfinally
не будет работать, если шнур питания оторван от стены.Ответы:
«Гарантированно» - это более сильное слово, чем любая реализация
finally
достоинств. Что гарантирует, что если исполнение вытекает из всегоtry
-finally
конструкт, он будет проходить через ,finally
чтобы сделать это. Что не гарантируется, так это то, что исполнение будет вытекать изtry
-finally
.A
finally
в генераторе или асинхронной сопрограмме может никогда не запуститься , если объект никогда не завершится. Это могло произойти множеством способов; вот один:Обратите внимание, что этот пример немного сложен: когда генератор собирает мусор, Python пытается запустить
finally
блок, выбрасываяGeneratorExit
исключение, но здесь мы перехватываем это исключение, а затемyield
снова, после чего Python выводит предупреждение («генератор проигнорировал GeneratorExit ") и сдаётся. Подробнее см. PEP 342 (сопрограммы через расширенные генераторы) .Другие причины, по которым генератор или сопрограмма могут не выполняться до завершения, включают, если объект просто никогда не GC'ed (да, это возможно, даже в CPython), или если
async with
await
s внутри__aexit__
, или если объектawait
s илиyield
s вfinally
блоке. Этот список не является исчерпывающим.A
finally
в потоке демона может никогда не выполняться, если сначала завершатся все потоки, не являющиеся демонами.os._exit
немедленно остановит процесс без выполненияfinally
блоков.os.fork
может заставитьfinally
блоки выполняться дважды . Помимо обычных проблем, которые можно ожидать от того, что происходит дважды, это может вызвать конфликты одновременного доступа (сбои, остановки и т. Д.), Если доступ к общим ресурсам не синхронизируется правильно .Поскольку
multiprocessing
использование раскошелиться-без-Exec для создания рабочих процессов при использовании вилки способа запуска (по умолчанию на Unix), а затем вызываетos._exit
в рабочий раз работа работника делается,finally
иmultiprocessing
взаимодействие может быть проблематичным ( пример ).finally
запуск блоков.kill -SIGKILL
предотвратитfinally
запуск блоков.SIGTERM
аSIGHUP
также предотвратитfinally
запуск блоков, если вы не установите обработчик для управления выключением самостоятельно; по умолчанию Python не обрабатываетSIGTERM
илиSIGHUP
.finally
может помешать завершению очистки. Один особо отметить случай , если пользователь нажимает Control-C только , как мы начинаем выполнятьfinally
блок. Python подниметKeyboardInterrupt
и пропустит каждую строкуfinally
содержимого блока. (KeyboardInterrupt
-безопасный код писать очень сложно).finally
блоки не запускаются.finally
Блок не является система транзакций; он не предоставляет гарантий атомарности или чего-либо в этом роде. Некоторые из этих примеров могут показаться очевидными, но легко забыть, что такие вещи могут случиться, иfinally
слишком сильно полагаться на них .источник
except
и никогда не ловитеGeneratorExit
внутри генератора. Ожидаются точки про потоки / убийство процесса / segfaulting / отключение питания, python не может творить чудеса. Также: исключения вfinally
явно являются проблемой, но это не меняет того факта, что поток управления был перемещен вfinally
блок. Что касаетсяCtrl+C
, вы можете добавить обработчик сигнала, который его игнорирует, или просто «планирует» полное завершение работы после завершения текущей операции.kill -9
что не указан язык. И, честно говоря, его нужно повторить, потому что он находится в слепой зоне. Слишком много людей забывают или не осознают, что их программа может быть остановлена как вкопанная, даже не получив разрешения на очистку.finally
блоки, как будто они предоставляют транзакционные гарантии. Может показаться очевидным, что это не так, но это не все понимают. Что касается случая генератора, есть много способов, которыми генератор может вообще не обрабатываться с помощью GC, и множество способов, которыми генератор или сопрограмма могут случайно сработать после,GeneratorExit
даже если они не улавливаютGeneratorExit
, например, еслиasync with
приостанавливается сопрограмма в__exit__
.Да. В итоге всегда побеждает.
Единственный способ победить это - остановить выполнение до того, как
finally:
появится возможность выполнить (например, вывести интерпретатор из строя, выключить компьютер, приостановить генератор навсегда).Вот еще пара, о которой вы, возможно, не думали:
В зависимости от того, как вы вышли из интерпретатора, иногда вы можете окончательно «отменить», но не так:
Использование ненадежного
os._exit
(на мой взгляд, это подпадает под "сбой интерпретатора"):В настоящее время я запускаю этот код, чтобы проверить, будет ли, наконец, выполняться после тепловой смерти Вселенной:
Однако я все еще жду результата, так что загляните сюда позже.
источник
finally
в генераторе или сопрограмме может довольно легко выйти из строя , не приближаясь к условию "сбой интерпретатора".sleep(1)
определенно приведет к неопределенному поведению. :-Dos._exit
для всех практических целей это то же самое, что вызвать сбой (нечистый выход). Правильный выход -sys.exit
.Согласно документации Python :
Следует также отметить, что при наличии нескольких операторов возврата, включая один в блоке finally, будет выполняться только возврат блока finally.
источник
Ну да и нет.
Гарантируется, что Python всегда будет пытаться выполнить блок finally. В случае, когда вы возвращаетесь из блока или вызываете неперехваченное исключение, блок finally выполняется непосредственно перед фактическим возвратом или вызовом исключения.
(что вы могли бы контролировать сами, просто запустив код в своем вопросе)
Единственный случай, который я могу представить, когда блок finally не будет выполнен, - это когда сам интерпретатор Python выйдет из строя, например, внутри кода C или из-за отключения электроэнергии.
источник
Я нашел это без использования функции генератора:
Сон может быть любым кодом, который может работать непостоянное количество времени.
Похоже, что здесь происходит то, что первый завершившийся параллельный процесс успешно покидает блок try, но затем пытается вернуть из функции значение (foo), которое нигде не было определено, что вызывает исключение. Это исключение уничтожает карту, не позволяя другим процессам достичь своих блоков finally.
Кроме того, если вы добавите строку
bar = bazz
сразу после вызова sleep () в блоке try. Затем первый процесс, достигший этой строки, выдает исключение (поскольку bazz не определен), которое вызывает запуск собственного блока finally, но затем убивает карту, в результате чего другие блоки try исчезают, не достигнув своих блоков finally, и первый процесс также не достигает своего оператора возврата.Для многопроцессорной обработки Python это означает, что вы не можете доверять механизму обработки исключений для очистки ресурсов во всех процессах, если хотя бы один из процессов может иметь исключение. Потребуется дополнительная обработка сигналов или управление ресурсами вне вызова карты многопроцессорности.
источник
Дополнение к принятому ответу, чтобы увидеть, как он работает, с несколькими примерами:
Это:
выведет
выведет
источник