raise
До сих пор ящики из нашего столбика трескались сами: исключение возбуждал Python, когда деление или int не удавались. Теперь функция подаст сигнал о поломке сама.
Представьте кассира в банке. Вы просите снять со счёта больше, чем на нём лежит. Кассир не отдаёт вам пустой конверт молча — вы могли бы его не открыть и уйти. Он отказывает вслух, так что не заметить отказ невозможно.
Функция тоже не всегда может выполнить то, о чём её просят. Вернуть в таком случае особое значение вроде None ненадёжно — вызывающий код может его не проверить. Инструкция raise возбуждает исключение, и функция сообщает об ошибке так же, как встроенные:
def withdraw(balance, amount):
if amount > balance:
raise ValueError("not enough money")
return balance - amount
print(withdraw(100, 30)) # 70
try:
print(withdraw(100, 500))
except ValueError as e:
print("error:", e) # error: not enough moneyДавайте разберём по шагам:
withdraw(100, 30)проходит проверку и возвращает70.withdraw(100, 500)видит, что500больше100, и выполняетraise.- Функция завершается сразу, а исключение уходит в
tryвокруг вызова. - Ветка
except ValueErrorпечатаетerror: not enough money.
Что пишут после raise
После raise пишут исключение. Возбудить можно только экземпляр BaseException или его потомка либо сам такой класс. Есть два способа.
Объект с сообщением
Чаще всего пишут класс с сообщением в скобках: raise ValueError("not enough money"). Python создаёт объект исключения, и сообщение потом показывают print(e) и последняя строка трассировки.
Класс без скобок
Можно написать и один класс, raise ValueError. Тогда Python сам создаст объект без сообщения. Так делают редко: без сообщения труднее понять, что пошло не так.
Класс выбирают по смыслу ошибки:
ValueError— неподходящее значение;TypeError— неподходящий тип.
Путь исключения
raise завершает функцию сразу, как return, но вместо значения наверх уходит исключение. Оно попадает в вызывающий код, оттуда — в код, вызвавший его, и так далее, пока не встретит try с подходящим except. Это тот же путь по столбику ящиков, что и у исключений самого Python.
Если такого try нет до самого верха, программа останавливается с трассировкой. Её последний вызов указывает на строку с raise, а последняя строка повторяет класс и сообщение. В первом примере withdraw(100, 500) не вернула значения, поэтому внешний print так и не выполнился.
Здесь аналогия с кассиром заканчивается: кассир отказывает только тому, кто стоит перед ним. Исключение же поднимается выше, пока кто-то на пути его не поймает.
Повторный raise
Иногда ошибку нужно заметить, но не обрабатывать до конца: напечатать, где она случилась, и передать дальше. Для этого внутри except пишут raise без аргументов. Это повторное возбуждение: то же исключение уходит наверх без изменений.
def parse(text):
try:
return int(text)
except ValueError:
print("cannot read", text) # cannot read x
raise
try:
parse("x")
except ValueError:
print("caller caught it") # caller caught itРазберём по шагам:
int("x")в функцииparseвозбуждаетValueError.- Ветка
exceptвparseпечатаетcannot read x. raiseбез аргументов возбуждает тот жеValueErrorснова, иparseзавершается.- Исключение ловит
tryвокруг вызова, и программа печатаетcaller caught it.
Без внешнего try программа остановилась бы с трассировкой, которая указывает на int(text). Повторное возбуждение сохраняет исходное место ошибки, и это важно при поиске причины.
Частая ошибка
После raise написали строку вместо исключения:
def withdraw(balance, amount):
if amount > balance:
raise "not enough money" # ✗
return balance - amount
withdraw(100, 500)TypeError: exceptions must derive from BaseExceptionСтрока — не исключение: её класс str не потомок BaseException, поэтому Python возбуждает вместо неё TypeError. Сообщение передают в скобках класса: raise ValueError("not enough money").
Главное
raiseвозбуждает исключение и сразу завершает функцию; наверх уходит исключение вместо значения.- После
raiseпишут экземпляр или класс исключения, обычно с сообщением:raise ValueError("not enough money"). raiseбез аргументов внутриexceptпередаёт то же исключение дальше без изменений.- Строка после
raiseдаётTypeError: возбудить можно только потомкаBaseException.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.