Определите, какой список напечатает программа и зачем в конструкции try нужен отдельный блок else.
steps = []
try:
steps.append("try")
result = 10 // 2
except ZeroDivisionError:
steps.append("except")
else:
steps.append(f"else:{result}")
finally:
steps.append("finally")
print(steps)
Программа напечатает ['try', 'else:5', 'finally']. Блок else выполняется только тогда, когда код в try завершился без исключения; блок finally выполняется в любом случае.
Конструкция try разделяет два разных сценария: обработку ошибки и действия, которые допустимы только после успешного выполнения опасной операции. Блок else позволяет не помещать последующий успешный код внутрь try, поэтому исключения из этого кода не будут ошибочно приняты за ошибки защищаемой операции.
Если весь последующий код находится в try, обработчик может перехватить исключение, возникшее уже после успешного вычисления результата. Это затрудняет диагностику: непонятно, сломалась исходная операция или логика обработки результата.
Например, преобразование данных и запись результата в журнал могут требовать разной реакции на ошибки. Блок else явно отделяет успешный путь от потенциально опасного действия.
Сначала Python выполняет тело try. В примере в список добавляется try, затем вычисляется 10 // 2, что даёт 5 и не вызывает ZeroDivisionError.
Поскольку исключения не было, блок except пропускается, а затем выполняется else. В него добавляется строка else:5. После завершения основной конструкции выполняется finally, поэтому в список попадает finally.
Порядок при отсутствии исключения таков:
try;else;finally.Если в try возникает исключение подходящего типа, выполняется соответствующий except, затем finally, а else пропускается. Если исключение не обработано, finally всё равно выполняется, после чего исключение продолжает распространяться.
Практическое ограничение: else не означает безусловно успешное завершение всей функции. Исключение внутри самого else не будет обработано соседним except, потому что обработчик относится к исключениям из тела try, а не из else.
Здесь будут напечатаны 42 с добавлением единицы, то есть 43, а затем очистка. Если заменить выражение в else на ошибочное, например result / 0, обработчик ValueError его не перехватит.
Сервис получает значение из конфигурации и после успешного разбора должен зарегистрировать результат. Один вариант — поместить разбор и журналирование в общий try. Его плюс — короткий код, но минус — обработчик ошибки разбора может скрыть ошибку журналирования.
Второй вариант — поместить журналирование в else. Тогда except отвечает только за ошибку разбора, а ошибка в журналировании сохраняет исходный контекст и может быть обработана отдельно на более подходящем уровне.
Выбранный вариант с else предпочтительнее: он уменьшает область действия обработчика и делает намерение кода очевидным. finally при этом можно использовать для закрытия файла, возврата соединения в пул или другой обязательной очистки.
Вопрос: Выполнится ли else, если в try возникло исключение, которое затем обработал except?
Ответ: Нет. Наличие самого исключения достаточно, чтобы пропустить else, даже если подходящий except успешно его обработал. После обработчика выполняется finally.
Вопрос: Что произойдёт, если исключение возникнет внутри else?
Ответ: Соседний except его не обработает. Область защиты except — это тело try; исключения из else распространяются наружу, если только их не перехватит внешний try или отдельная вложенная конструкция.
Вопрос: В каком порядке выполняются else и finally, если в try есть return?
Ответ: При успешном завершении try сначала выполняется else, затем finally, и только после этого фактически завершается возврат из функции. Если finally содержит собственный return или выбрасывает исключение, он может заменить исходный результат или исключение, поэтому такие конструкции используют с осторожностью.