Перейти к содержимому
Теория · Исключения · урок 6 из 11

raise

До сих пор ящики из нашего столбика трескались сами: исключение возбуждал Python, когда деление или int не удавались. Теперь функция подаст сигнал о поломке сама.

Представьте кассира в банке. Вы просите снять со счёта больше, чем на нём лежит. Кассир не отдаёт вам пустой конверт молча — вы могли бы его не открыть и уйти. Он отказывает вслух, так что не заметить отказ невозможно.

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

Python
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

Давайте разберём по шагам:

  1. withdraw(100, 30) проходит проверку и возвращает 70.
  2. withdraw(100, 500) видит, что 500 больше 100, и выполняет raise.
  3. Функция завершается сразу, а исключение уходит в try вокруг вызова.
  4. Ветка 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 без аргументов. Это повторное возбуждение: то же исключение уходит наверх без изменений.

Python
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

Разберём по шагам:

  1. int("x") в функции parse возбуждает ValueError.
  2. Ветка except в parse печатает cannot read x.
  3. raise без аргументов возбуждает тот же ValueError снова, и parse завершается.
  4. Исключение ловит try вокруг вызова, и программа печатает caller caught it.

Без внешнего try программа остановилась бы с трассировкой, которая указывает на int(text). Повторное возбуждение сохраняет исходное место ошибки, и это важно при поиске причины.

Частая ошибка

После raise написали строку вместо исключения:

Python
def withdraw(balance, amount):
    if amount > balance:
        raise "not enough money"  # ✗
    return balance - amount


withdraw(100, 500)
TEXT
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 и подпишись на канал — откроются все уроки.

Войти другим способом