Menu
Русский

Исключения в Python: try, except, else, finally и raise

Как обрабатывать ошибки в Python — try/except/finally, ловля конкретных исключений, собственные raise и когда дать ошибке всплыть.

Ошибки — это просто значения с плохим настроением

Когда в Python что-то идёт не так — деление на ноль, чтение пропавшего файла, парсинг плохого числа — рантайм создаёт объект исключения и начинает раскручивать стек вызовов, пока его кто-то не поймает. Если никто не ловит — программа завершается и печатает трейсбэк.

Исключения сами по себе — не плохо. Это способ Python сказать «эту операцию я продолжать не могу, вот почему». Твоя работа — в каждом случае решить, какие из них ты умеешь обработать, а каким дать развиться дальше.

Базовая форма

main.py
Output
Click Run to see the output here.
  • try: открывает блок рискованного кода.
  • except ValueError: ловит это конкретное исключение, если оно вылетело внутри try.
  • Если исключения не было, except полностью пропускается.

Запусти сниппет с 42 — работает. Запусти с hello — сработает обработчик.

Ловим конкретные исключения

У Python иерархия типов исключений. Те, что встретишь часто:

  • ValueError — значение в каком-то смысле неправильное (int("abc"), выход за диапазон).
  • TypeError — не тот тип ("hi" + 3).
  • KeyError — ключ в словаре не найден.
  • IndexError — индекс вышел за диапазон последовательности.
  • FileNotFoundError — файл не существует.
  • ZeroDivisionError — попытка поделить на ноль.
  • AttributeError — у объекта нет запрошенного атрибута.

Лови конкретное, которое умеешь обработать:

main.py
Output
Click Run to see the output here.

Несколько исключений в одной ветке — через кортеж:

main.py
Output
Click Run to see the output here.

Заметь as e. Привязывает объект исключения к e, чтобы можно было посмотреть его сообщение или атрибуты.

Не лови всё подряд

Голый except: ловит буквально всё, включая KeyboardInterrupt (твой Ctrl-C) и системные выходы. Не используй.

except Exception: чуть лучше, но всё равно опасен — он глотает баги, которых ты не ждал, и прячет истинный источник проблем:

# Don't do this without a really good reason.
try:
    do_something()
except Exception:
    pass

Правильный ход — почти всегда ловить конкретное исключение, которое ты умеешь обработать. Если непредусмотренное исключение добралось до вершины программы, трейсбэк точно скажет, что пошло не так, — это фича, а не баг.

else и finally

У try есть две дополнительные необязательные ветви:

  • else выполняется, если блок try завершился без исключения.
  • finally выполняется в любом случае — было исключение или нет.
main.py
Output
Click Run to see the output here.

else — самое чистое место для «кода только при успехе»: не надо заставлять try делать больше, чем ту часть, которая реально может упасть. finally — для уборки, которая должна сработать, даже если try упал: закрыть ресурс, отпустить блокировку, восстановить состояние.

Поднимаем исключения сами

raise сигнализирует об ошибке в твоём собственном коде:

main.py
Output
Click Run to see the output here.

Выбирай тип исключения, подходящий к тому, что пошло не так. Сначала тянись к встроенным — ValueError, TypeError, FileNotFoundError, — а потом уже определяй свой класс.

Свои исключения

Когда встроенные не ухватывают смысл, определи собственное:

main.py
Output
Click Run to see the output here.

Наследуй от Exception (или более конкретного встроенного) и дай классу docstring. Обычно этого достаточно. Собственные исключения позволяют вызывающему ловить именно ту ошибку, которая осмысленна для его домена.

raise ... from ...: цепочки исключений

Когда одно исключение порождает другое — сохраняй цепочку:

main.py
Output
Click Run to see the output here.

from e прикрепляет исходную ошибку. При печати трейсбэка Python показывает обе — всплывшую ConfigError и породившую её FileNotFoundError. Такой след бесценен при отладке.

Контекстные менеджеры: чистый способ прибираться

finally — нормально, но для ресурсов вроде файлов контекстный менеджер (что и использует with) почти всегда лучше:

# finally version
f = open("data.txt")
try:
    data = f.read()
finally:
    f.close()

# with version
with open("data.txt") as f:
    data = f.read()

Оба безопасны. Форма with короче и применяется автоматически. Тянись к finally, только когда делаешь что-то, чего стандартная библиотека ещё не завернула в контекстный менеджер.

Когда не надо ловить

Поймать исключение — это решение: ты говоришь «я умею это обработать». Не умеешь — дай ему идти наверх. Такой код почти всегда ошибка:

try:
    do_work()
except Exception:
    pass  # silently ignore everything

Глушить ошибки — делать баги невидимыми. Лучше громко упасть, чем ковылять в несогласованном состоянии.

Подводим итог

  • try/except обрабатывает ошибки, от которых можно восстановиться.
  • Лови конкретные исключения, не Exception оптом.
  • raise сигнализирует об ошибках в твоём коде.
  • Блоки with заменяют большую часть уборки через finally.
  • Когда сомневаешься — пусть исключение всплывает.

Дальше — обзор конкретных ошибок, которые Python поднимает чаще всего — KeyError, ValueError, ModuleNotFoundError и пара других — плюс отладочные привычки, быстро их чинящие.

Часто задаваемые вопросы

Как обрабатывать ошибки в Python?

Оберни рискованный код в блок try и лови конкретное исключение в блоке except: try: risky() except ValueError: .... Необязательный else выполняется, если исключения не было; finally — в любом случае, для уборки.

Не лучше ли ловить Exception для надёжности?

Нет. Голый except: или except Exception: прячет непредусмотренные баги. Лови конкретное исключение, которое ты умеешь обработать, а всё остальное пусть всплывает — так ты увидишь настоящую проблему.

В чём разница между raise и raise from?

raise NewError(...) поднимает новое исключение. raise NewError(...) from original привязывает исходное исключение как причину, и Python показывает его в трейсбэке. Используй from, когда низкоуровневая ошибка породила более высокоуровневую, которую ты хочешь выставить наружу.

Учитесь программировать с Coddy

НАЧАТЬ