Ошибки и отладка

Обработка исключений через try-except

Содержание курса

Перехват конкретного типа исключения

Можно написать except: без указания типа — это работает, но это грубый инструмент. На практике тебе нужно знать не просто «что-то пошло не так», а именно что: пользователь ввёл не число или кто-то делит на ноль. Для этого после except указывают конкретный класс исключения:

except ValueError:
    ...

При такой записи блок сработает только если возникло именно ValueError. Любое другое исключение — ZeroDivisionError, TypeError, что угодно — пройдёт мимо этого except и либо будет поймано следующим обработчиком, либо уронит программу с traceback. Типизированный except — это фильтр, а не универсальный пылесос. Почему голый except: без типа опасен — разберём подробно в секции про типичные ошибки; пока сосредоточимся на том, как правильно указывать тип.

Пример 1: ValueError при вводе числа

int() вызывает ValueError, когда строка не является целым числом. Именно это происходит, если пользователь вводит "abc" или "12.5":

try:
    n = int(input("Введите целое число: "))
    print("Вы ввели:", n)
except ValueError:
    print("Ошибка: нужно ввести целое число, а не текст")

print("Программа продолжает работу")

Если пользователь вводит "abc"int("abc") бросает ValueError, Python немедленно прыгает в except ValueError:, печатает сообщение, и программа продолжается. Строка print("Вы ввели:", n) при этом не выполняется вовсе — она осталась внутри try после точки падения.

Если пользователь вводит 42ValueError не возникает, except пропускается, оба print отрабатывают нормально.

Пример 2: ZeroDivisionError при делении

Деление на ноль — отдельный тип исключения, и для него нужен свой except:

a = 10
b = 0

try:
    result = a / b
    print("Результат:", result)
except ZeroDivisionError:
    print("Ошибка: делить на ноль нельзя")

print("Вычисление завершено")

При b = 0 строка a / b бросает ZeroDivisionError, блок except ZeroDivisionError: перехватывает его. При b = 5 исключения нет, результат печатается нормально, except не трогается.

Обрати внимание: except ValueError: не поймал бы ZeroDivisionError — и наоборот. Каждый except отвечает ровно за свой тип. Это и есть смысл типизированного перехвата: ты точно знаешь, какую именно ситуацию обрабатываешь, и пишешь осмысленное сообщение именно под неё.

Если нужно обработать оба типа в одном блоке try, можно написать два except подряд:

try:
    n = int(input("Введите делитель: "))
    result = 100 / n
    print("100 /", n, "=", result)
except ValueError:
    print("Ошибка: введите число, а не текст")
except ZeroDivisionError:
    print("Ошибка: делить на ноль нельзя")

Python проверяет except-блоки сверху вниз и заходит в первый подходящий. Если пользователь вводит "abc" — срабатывает первый. Если вводит 0 — срабатывает второй. Это даёт разные, точные сообщения для разных ситуаций — именно то, что нужно пользователю.