Программирование и IT
Python: исключения
Исключение — сообщение о том, что выполнение пошло не так, как предполагал код. По умолчанию оно останавливает программу и печатает трассировку. Конструкция `try/except` позволяет перехватить только ожидаемую ошибку и продолжить работу, не пряча при этом настоящие поломки.
В этой статье
Как выглядит ошибка и что в ней читать#
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# ValueError: invalid literal for int() with base 10: 'сто'
В трассировке важны две вещи: последняя строка — тип и текст ошибки, а строки
выше — путь, по которому до неё добрались. Читать её нужно снизу вверх: сначала
что случилось, потом где.
Типы исключений образуют дерево. Почти всё, что ловят в прикладном коде,
наследуется от Exception:
ValueError— тип подходит, значение нет (int("сто"));TypeError— тип не подходит ("a" + 1);KeyErrorиIndexError— нет такого ключа или индекса;ZeroDivisionError— деление на ноль;FileNotFoundError— нет файла;AttributeError— у объекта нет такого атрибута.
try/except: минимум#
=
= 0
Блок try содержит только рискованную строку — чем он короче, тем точнее видно,
что именно защищено. Типов можно указать несколько, а сам объект ошибки
доступен через as:
=
# KeyError — 'b'
Несколько блоков except проверяются сверху вниз, и срабатывает первый
подходящий. Поэтому частные типы пишут выше общих: except Exception первым
перехватит всё и до остальных дело не дойдёт.
else и finally#
=
# только если исключения не было
# разобрали: 42
# этот блок выполнится всегда
else выполняется, когда try прошёл без ошибок, — туда выносят код, который
не должен быть защищён этим except. finally выполняется в любом случае: и
при ошибке, и при return, и при выходе по исключению дальше. В нём закрывают
ресурсы — хотя для файлов и соединений удобнее оператор with, описанный в
статье про чтение файла.
raise и свои классы ошибок#
Бросить исключение самому — одна строка. Это правильный способ сообщить о
недопустимых аргументах: функция не должна молча возвращать None.
return -
# отказ: сумма должна быть положительной, получено -5
Когда стандартных типов не хватает, заводят свой — обычно пустым классом:
"""Настройки заданы неверно."""
# в настройках нет ключа host
Перехватить и перебросить с сохранением причины помогает raise ... from:
raise ConfigError("не прочитали") from error — в трассировке останутся оба
исключения, и видно, что было первопричиной.
Чего не делать#
except: без типа. Такой блок ловит вообще всё, включая KeyboardInterrupt
и SystemExit, — программу становится невозможно прервать с клавиатуры. Если
нужна широкая сеть, пиши except Exception, и лучше с указанием, что
случилось.
except ...: pass. Молча проглоченная ошибка возвращается через час в виде
пустого отчёта, и найти её причину уже нечем. Минимум — напечатать или записать
в журнал.
Огромный try. Когда под try двадцать строк, except ValueError
относится ко всем двадцати, и ты перехватишь ошибку совсем не там, где ждал.
Исключения вместо условий. Проверка if x > 0 не нуждается в try.
Но и обратная крайность вредна: в Python принято пробовать и ловить, а не
заранее опрашивать состояние — d.get(key) и try/except KeyError лучше, чем
if key in d перед каждым обращением, когда обращений много.
Практика: напиши функцию safe_div(a, b), которая возвращает частное, а при
делении на ноль — None с сообщением; затем функцию parse_ages(строки),
которая превращает список строк в числа, пропуская нечисловые и сообщая о
каждом пропуске. Пригодятся списки и
функции.
План по этапам
- Прочитать трассировкуВыполнить int("сто") и разобрать вывод: тип, текст, место.
- Первый try/exceptЗащитить преобразование строки в число и выдать понятное сообщение вместо падения.
- Несколько типовПерехватить KeyError и ZeroDivisionError одним блоком, напечатать тип ошибки через type(error).__name__.
- Добавить finallyУбедиться, что блок finally выполняется и при ошибке, и без неё.
- Свой класс ошибкиЗавести наследника Exception, бросить его из функции и поймать в вызывающем коде.
Начать изучать эту тему у себя
План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.
Проверь себя
1.Какой тип исключения даст int("сто")?
2.Что выведет: try: x = 1 / 0 / except ZeroDivisionError: x = 7 / finally: x += 1 — чему равен x?
3.Какой блок выполняется только тогда, когда в try не было исключения?
4.Чем плох except: без указания типа?
Источники
-
Ошибки и исключения в учебнике Pythontry, except, else, finally и raise с примерамибесплатно
-
Встроенные исключенияПолное дерево типов: что от чего наследуетсябесплатно
-
StepikКурсы по Python с задачами на обработку ошибокбесплатно
Было полезно?