Программа Python перехватывает ожидаемую ошибку и сохраняет понятную причину
Программирование

Ошибки и исключения в Python

Читаем traceback, различаем ожидаемый неверный ввод и дефект программы, обрабатываем конкретные исключения и сохраняем полезную причину ошибки.

Содержание

Исключение — объект, который сообщает, что текущую операцию нельзя нормально завершить. Неверный ввод не превращается в число, файл отсутствует, сеть недоступна или функция получила недопустимое значение. Если исключение не обработано, Python останавливает программу и печатает traceback.

Это продолжение урока о функциях и raise. Цель — не убрать красный текст любой ценой, а отличить ожидаемую ситуацию от дефекта и сохранить понятную причину.

Сначала прочитайте исходную ошибку

Создайте errors.py с намеренно неверным преобразованием. Код безопасен и нужен, чтобы увидеть структуру сообщения:

quantity_text = "три"
quantity = int(quantity_text)

print(quantity)

Запуск завершится ValueError с пояснением, что строку три нельзя преобразовать в целое число. Строка print не выполнится. В traceback последняя строка содержит тип и сообщение, выше показан путь файла и номер строки.

Начинайте чтение снизу, затем найдите последнюю строку собственного кода. В большой программе верхние кадры показывают, какие функции вызвали друг друга. Не публикуйте необработанный traceback пользователю веб-сайта: в нём могут быть внутренние пути и детали реализации, но сохраните его в защищённом журнале для диагностики.

try ограничивает ожидаемо рискованную операцию

Если текст приходит от пользователя, неверный формат предсказуем. Оберните только преобразование, а не весь файл:

quantity_text = input("Количество: ")

try:
    quantity = int(quantity_text)
except ValueError:
    print("Введите целое число, например 3")
else:
    print(f"Принято: {quantity}")

При вводе 3 выполняется блок else и появляется подтверждение. При вводе три срабатывает конкретный except ValueError, программа показывает понятную подсказку и завершается без traceback. Никакие файлы не меняются.

В try находится только операция, от которой ожидается ValueError. Если туда поместить десятки строк, обработчик может принять за неверный ввод совершенно другую ошибку того же типа.

Перехватывайте конкретный тип

Запись except: ловит почти все сигналы, включая попытку пользователя остановить программу. except Exception: немного уже, но всё ещё объединяет сетевую ошибку, дефект индекса и неверную конфигурацию.

Правильный вопрос звучит так: «Какой именно сбой здесь ожидаем и что программа умеет после него сделать?» Для отсутствующего необязательного файла это может быть FileNotFoundError и переход к значениям по умолчанию. Для обязательной конфигурации разумнее вывести путь и завершиться с ошибкой, а не продолжать с выдуманными параметрами.

Несколько ожидаемых типов можно обработать отдельными ветвями:

try:
    position = int(input("Номер сервера: "))
    server = ["web-01", "db-01"][position]
except ValueError:
    print("Номер должен быть целым числом")
except IndexError:
    print("Сервера с таким номером нет")
else:
    print(server)

Текст вызывает ValueError, а число за границами списка — IndexError. В допустимом случае выполняется else. Отдельные сообщения помогают пользователю исправить именно его ввод.

Сохраняйте исходную причину

При повторном выбрасывании исключения добавляйте предметный контекст и связывайте причины через from:

def parse_port(text):
    try:
        port = int(text)
    except ValueError as error:
        raise ValueError(f"Порт должен быть целым числом: {text!r}") from error

    if not 1 <= port <= 65535:
        raise ValueError(f"Порт вне диапазона: {port}")
    return port

Функция либо возвращает допустимый порт, либо создаёт понятный ValueError. Запись {text!r} показывает строковое представление с кавычками, поэтому видны пустой ввод и пробелы. Конструкция from error сохраняет первоначальное исключение в цепочке traceback.

Числовая граница здесь является свойством поля TCP/UDP-порта. Для других данных диапазон должен исходить из их собственного правила, а не копироваться из примера.

finally выполняется при любом исходе

Блок finally запускается после try независимо от успеха и исключения. Он подходит для освобождения ресурса, который был получен до рискованной операции. Однако для файлов и блокировок чаще используйте with: менеджер контекста автоматически выполняет выход и уменьшает вероятность забыть закрытие.

Не помещайте в finally действие, которое может скрыть исходную ошибку новым исключением. Очистка должна быть небольшой и надёжной, а её собственные сбои — журналироваться отдельно.

Не используйте исключение вместо обычного условия

Если пустой список является нормальным состоянием, его можно проверить через if not servers. Исключение уместно, когда операция не выполнила свой договор. Но в Python некоторые операции естественно сообщают об отсутствии через исключение, например прямой доступ к неизвестному ключу словаря.

Выбирайте один понятный интерфейс функции: вернуть None для ожидаемого отсутствия либо поднять конкретное исключение. Не заставляйте вызывающий код угадывать по случайному сочетанию.

Ошибка должна приводить к определённому завершению

Консольный инструмент при неуспехе должен вернуть ненулевой код выхода, чтобы планировщик или CI заметил сбой. Пользователь получает короткое объяснение, а технический журнал — traceback и контекст. Продолжать работу можно только если состояние известно и следующий шаг безопасен.

Не пишите except Exception: pass. Молчаливый перехват оставляет программу с отсутствующими данными и переносит сбой в менее понятное место. Если пока неизвестно, как исправиться, дайте исключению подняться или завершите программу явно после регистрации причины.

Следующий урок — модули и импорт: он разнесёт проверенные функции по файлам. Повторите структуры данных и сверяйте детали с официальным разделом Errors and Exceptions.

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Что перехватывать при int('abc')?
02С какой части traceback начинать чтение?
03Почему except Exception: pass опасен?

Разбираем коротко

Частые вопросы

Нужно ли оборачивать всю программу в try/except?

Нет. Перехватывайте конкретную ожидаемую ошибку там, где программа знает, как корректно продолжить или завершиться. Общий перехват скрывает дефекты и усложняет диагностику.

Что такое traceback?

Это цепочка вызовов от точки запуска до строки, где исключение осталось необработанным. Читать её обычно начинают с последней строки: там тип и сообщение ошибки.

Когда использовать finally?

Когда действие должно выполниться при любом исходе, например освобождение внешнего ресурса. Для файлов предпочтителен менеджер контекста with, который закрывает ресурс автоматически.