Обработка исключений через try-except
Содержание курса
Типичные ошибки при использовании try-except
Когда знаешь, как работает try-except, легко допустить два антипаттерна, которые не ломают программу сразу, но делают её опасной и трудноотлаживаемой.
Антипаттерн 1: except без указания типа
Выглядит безобидно:
try:
n = int(input("Введите число: "))
result = 100 / n
print("Результат:", result)
except:
print("Что-то пошло не так")
Проблема в том, что голый except: перехватывает буквально всё — включая исключения, о которых ты даже не подозревал. Предположим, внутри try есть опечатка в имени переменной: Python бросит NameError, и твой except молча его проглотит. В выводе появится «Что-то пошло не так» вместо нормального traceback, и ты потратишь час, выясняя, почему программа ведёт себя не так, как ожидалось.
Ещё хуже: except: ловит даже KeyboardInterrupt — то есть, если пользователь нажмёт Ctrl+C, чтобы остановить программу, она не остановится. Это уже не просто неудобство.
Исправление — всегда указывай конкретный тип и держи в try только опасные строки:
result = None
try:
n = int(input("Введите число: "))
result = 100 / n
except ValueError:
print("Ошибка: введите целое число")
except ZeroDivisionError:
print("Ошибка: делить на ноль нельзя")
if result is not None:
print("Результат:", result)
Теперь любое неожиданное исключение — NameError, TypeError, что угодно — не проглатывается, а выдаёт traceback. Это именно то, что нужно при разработке: видеть реальные ошибки, а не прятать их.
Антипаттерн 2: слишком широкий блок try
Вторая ловушка — запихнуть в try большой кусок кода:
try:
print("Начинаем вычисление") # безопасная строка — зря в try
n = int(input("Введите делитель: "))
result = 100 / n
print("Результат:", result) # не выполнится при ошибке
print("Всё готово") # не выполнится при ошибке
except ValueError:
print("Ошибка: введите целое число")
Если int(input(...)) бросает ValueError, Python немедленно прыгает в except — и все строки после проблемной внутри try не выполняются. Оба print в конце просто пропускаются. Чем больше try, тем труднее понять, откуда именно прилетело исключение — особенно если в блоке несколько мест, которые теоретически могут упасть с одним и тем же типом ошибки.
Исправление — вынести безопасные строки наружу, оставив в try только то, что действительно может бросить исключение:
result = None # объявляем заранее — на случай, если try не завершится успешно
print("Начинаем вычисление") # ← вынесли: не может вызвать исключение
try:
n = int(input("Введите делитель: "))
result = 100 / n
except ValueError:
print("Ошибка: введите целое число")
except ZeroDivisionError:
print("Ошибка: делить на ноль нельзя")
if result is not None:
print("Результат:", result) # ← вынесли; выполняется только при успехе
print("Всё готово") # ← выполняется всегда, при любом исходе
Обрати внимание на result = None перед блоком: если внутри try возникнет исключение, переменная result не получит значения от деления. Без предварительного объявления строка print("Результат:", result) после блока вызвала бы NameError — программа снова упала бы, только уже по другой причине. Именно поэтому переменные, которые заполняются внутри try, нужно объявлять с безопасным начальным значением до блока.
Правило одно: в try — только тот минимальный код, который непосредственно может вызвать перехватываемое исключение. Всё остальное — снаружи.
