ПрограммированиеBackend разработчик

Опишите механизмы обработки исключений в Python. Как работает блок try/except/finally, для чего нужен raise, и какие нередко встречаются подводные камни?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В Python обработка исключений реализуется через конструкции try/except/else/finally и оператор raise.

try определяет защищаемый блок кода. Если внутри возникает исключение, управление передаётся в ближайший подходящий блок except. Если исключения нет — выполняется блок else, если он есть. Блок finally исполняется всегда — независимо от того, было исключение или нет (например, для освобождения ресурсов).

Оператор raise вызывает исключение явно или повторяет текущее (в блоке except без указания типа).

Пример:

try: x = 1 / 0 except ZeroDivisionError as e: print(f"Ошибка: {e}") else: print("Ошибки не было") finally: print("Всегда исполняется")

Вывод:

Ошибка: division by zero
Всегда исполняется

Тонкости:

  • При вложенных обработчиках важен порядок except: от частных типов к более общим (иначе частные не будут обработаны).
  • Блок finally не может предотвратить повторный выброс исключения (если внутрь finally вставить raise — актуальна самая "последняя" ошибка).

Вопрос с подвохом

Вопрос: «Что будет, если и в блоке try, и в finally вызвать raise разные исключения?»

Ответ: Исключение из finally "перекроет" исключение из try: наружу выйдет то, что вылетело в finally.

def foo(): try: raise ValueError("в try") finally: raise IndexError("в finally") try: foo() except Exception as e: print(repr(e)) # Выведет: IndexError('в finally')

Примеры реальных ошибок из-за незнания тонкостей темы


История

В ETL-процессе в блоке finally безусловно закрывалось соединение с базой, но забывали, что при этом в finally может вылетать исключение (например, если соединение уже закрыто). Итог — "скрытое" исключение из finally полностью поглощало исключение из основного кода, резко усложняя отладку.


История

Использовали цепочки нескольких except: от общего except Exception выше частных. В результате все специфические except оставались "мертвыми" — мелкоуровневые исключения не ловились отдельно, что затрудняло обработку специфических ошибок.


История

В web-сервисе в except блока забывали пробрасывать исключение дальше через "raise", логируя ошибку, но позволяя выполнению идти дальше. В итоге реальные ошибки "терялись", а программа продолжала работать с некорректным состоянием.