Урок курса
Обработка исключений через try-except
Python для новичковВ прошлом уроке мы научились читать traceback: находить строку с ошибкой, определять её тип и понимать, что пошло не так. Теперь следующий шаг — не просто читать ошибку, а перехватывать её прямо во время выполнения, чтобы программа не падала.
Зачем нужна обработка исключений: идея try-except
Когда Python встречает операцию, которую не может выполнить — например, деление на ноль или преобразование строки в число — он генерирует исключение. Если это исключение никто не обрабатывает, интерпретатор останавливает программу и печатает traceback. Для скрипта, который запускают один раз и смотрят глазами, это терпимо. Для программы, которая взаимодействует с пользователем или обрабатывает внешние данные, — катастрофа: одна неожиданная строка ввода роняет всё.
Конструкция try-except решает эту проблему. Идея простая: ты заранее говоришь Python, что определённый кусок кода может упасть, и указываешь, что делать в этом случае.
Структурно это выглядит так:
try:
# код, который может вызвать исключение
except ТипИсключения:
# что делать, если исключение возникло
Поток управления здесь работает строго:
Python начинает выполнять инструкции внутри
try— по одной, сверху вниз.Как только одна из них вызывает исключение — выполнение
tryнемедленно прерывается. Всё, что было после проблемной строки внутриtry, не выполняется.Управление переходит в блок
except, и Python выполняет то, что там написано.После
exceptпрограмма продолжает работу в обычном режиме — она не падает.
Если же внутри try всё прошло без исключений, блок except просто пропускается.
Важно понять: try и except — это не два независимых инструмента, а одна конструкция. try без except — синтаксическая ошибка. Они всегда работают в паре: первый определяет зону риска, второй — запасной выход.
Обрати внимание: после except рекомендуется указывать конкретный тип исключения — ValueError, ZeroDivisionError и так далее. Почему это важно — разберём в следующей секции. Пока запомни саму механику: при ошибке внутри try Python немедленно переходит в 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 после точки падения.
Если пользователь вводит 42 — ValueError не возникает, 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 — срабатывает второй. Это даёт разные, точные сообщения для разных ситуаций — именно то, что нужно пользователю.
Процедура написания try-except: пошаговый алгоритм
Концепции разобраны, примеры показаны — теперь зафиксируем, как именно действовать, когда садишься писать try-except с нуля.
Шаг 1. Найди опасное место
Сначала спроси себя: какая конкретная строка может вызвать исключение? Это не всегда очевидно, но обычно подозреваемые — это:
- вызов
int()илиfloat()на пользовательском вводе; - деление, где знаменатель может оказаться нулём;
- обращение к внешним данным, формат которых заранее неизвестен.
Найди именно эту строку — не функцию целиком, не блок из десяти строк, а конкретную операцию.
Шаг 2. Оберни минимальный код в try
Поместить в try нужно ровно столько, сколько необходимо — только те строки, которые сами могут бросить исключение. Если опасные строки — это int(input(...)) и деление, то в try идут именно они:
try:
n = int(input("Введите делитель: "))
result = 100 / n
Здесь ровно две строки: первая может дать ValueError, вторая — ZeroDivisionError. print с результатом выносим наружу — он безопасен и не должен находиться под защитой.
Шаг 3. Напиши except с конкретным типом
После try — один или несколько блоков except, каждый с конкретным типом:
except ValueError:
print("Ошибка: введите целое число")
except ZeroDivisionError:
print("Ошибка: делитель не может быть нулём")
Каждое сообщение объясняет именно ту ситуацию, которую перехватывает. Пользователь видит понятный текст, а не Python traceback.
Шаг 4. Убедись, что код после блока продолжает работу
Поставь после всей конструкции строку, которая должна выполниться в любом случае — и проверь, что она действительно выполняется при обоих исходах:
result = None
try:
n = int(input("Введите делитель: "))
result = 100 / n
except ValueError:
print("Ошибка: введите целое число")
except ZeroDivisionError:
print("Ошибка: делитель не может быть нулём")
if result is not None:
print("Результат:", result)
print("Готово") # выполняется всегда
Запусти программу дважды: один раз введи нормальное число, второй — что-нибудь неправильное. В обоих случаях print("Готово") должен появиться в выводе. Если не появляется — где-то внутри try осталось необработанное исключение другого типа.
Шаг 5. Перечитай try и убедись, что он не раздулся
Это финальная проверка: пройдись по строкам внутри try и спроси себя, может ли каждая из них вызвать исключение того типа, который ты обрабатываешь. Если какие-то строки там явно безопасны и не зависят от опасной операции — вынеси их наружу. Чем уже 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 — только тот минимальный код, который непосредственно может вызвать перехватываемое исключение. Всё остальное — снаружи.
Задача 1
Посмотрите на код:
try:
x = int(input('Введите число: '))
print(x * 2)
except ValueError:
print('Некорректный ввод')
Пользователь вводит строку 'привет'. Что выведет программа?
Задача 2
Что произойдёт, если в блоке try возникнет исключение типа TypeError, а в except указан только ValueError?
try:
result = 'цена: ' + 100
except ValueError:
print('Поймано!')
Задача 3
Что выведет следующий код, если пользователь вводит строку '0'? Напишите вывод точно так, как он появится в терминале.
try:
n = int(input('Введите число: '))
print(100 / n)
except ValueError:
print('Нужно ввести число')
except ZeroDivisionError:
print('Ноль не подходит')
Задача 4
Исправь обработку двух типов ошибок в оплате
В стартовом коде есть функция check_payment(a_str, b_str), которая принимает две строки, преобразует их в целые числа и должна вернуть результат целочисленного деления a // b.
Сейчас в коде обработан только один тип ошибки, поэтому программа не умеет корректно реагировать на деление на ноль.
Исправьте функцию так, чтобы она:
- возвращала
a // b, если обе строки успешно преобразовались в числа и делитель не равен нулю; - возвращала строку
Ошибка: некорректное число, еслиint()не смог преобразовать одну из строк; - возвращала строку
Ошибка: деление на ноль, если делитель оказался равен0.
После функции не меняйте общий смысл программы: внизу уже должны быть вызовы check_payment('18', '3'), check_payment('12', '0'), check_payment('x', '5').
Исправьте именно обработку исключений.
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 5
Безопасный расчёт скорости по двум введённым значениям
Напишите программу safe_speed.py.
Пользователь вводит две строки по порядку: расстояние в километрах и время в часах.
Программа должна:
- в блоке
tryпреобразовать обе строки в целые числа; - вычислить скорость по формуле
distance // time; - вывести результат;
- если одна из строк не является целым числом, вывести
Ошибка: некорректное число; - если время равно нулю, вывести
Ошибка: деление на ноль.
После обработки ошибки программа должна завершаться без traceback.
Формат ввода: две строки, сначала расстояние, затем время.
Формат вывода: одно сообщение или одно число.
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 6
Безопасное деление двух чисел из строк
Напишите функцию safe_int_divide(a_str, b_str), которая принимает две строки.
Функция должна:
- внутри блока
tryпреобразовать обе строки в целые числа черезint(); - выполнить целочисленное деление
a // bи вернуть результат; - если при преобразовании возникла ошибка, перехватить
ValueErrorи вернуть строкуОшибка: некорректное число; - если при делении возникла ошибка, перехватить
ZeroDivisionErrorи вернуть строкуОшибка: деление на ноль.
Функция ничего не печатает, а только возвращает значение.
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задачи к уроку
- Почему except ValueError не перехватывает TypeError в Python
Разберите, что случится с TypeError внутри try, если обработчик ожидает только ValueError. Практическая задача с коротким фрагментом кода.
- Python try-except: ValueError и деление на ноль
Рассчитайте скорость по введённым строкам и обработайте ValueError и ZeroDivisionError. Практическая задача онлайн.
- Python try-except: ValueError и ZeroDivisionError
Напишите безопасное деление строковых чисел с обработкой ValueError и ZeroDivisionError. Практическая задача с проверкой.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
