Генерация исключений при помощи raise

Раздел: Управление выполнением -> Вызов и обработка исключений

Основы использования raise

В Python оператор raise позволяет программисту явно вызвать исключение. Это бывает необходимо, когда код обнаруживает недопустимую ситуацию, которую не может обработать самостоятельно. Основная цель raise - сигнализировать об ошибке и передать управление соответствующему обработчику.

Основной способ вызова исключения - указать класс исключения и, при необходимости, сообщение:

raise ValueError("Недопустимое значение")

вызов ошибки python (вызов ошибки (raise) в python)

После выполнения этой строки программа немедленно прерывает нормальный поток и ищет подходящий блок except. Если блок не найден, выполнение завершается с сообщением об ошибке.

Цель: явно сигнализировать об ошибке, которую невозможно обработать локально.

Как вызвать исключение только по классу, без сообщения?

Можно передать только класс исключения. При этом будет создан экземпляр без аргументов:

raise ValueError

Такой способ удобен, когда сообщение не требуется или исключение будет обработано по типу.

Проблема: при отладке сложнее понять причину ошибки, если сообщение отсутствует. Решение - добавлять хотя бы краткое описание.

Как повторно возбудить перехваченное исключение?

Внутри блока except можно использовать raise без аргументов - это перебросит текущее исключение дальше по стеку:

try:
    x = int("abc")
except ValueError:
    print("Логирование...")
    raise

Это позволяет выполнить дополнительную обработку (например, запись в лог), но не скрывать ошибку от вышестоящего кода.

Типичная ошибка: использование raise без аргумента вне блока except вызывает RuntimeError. Решение - всегда проверять контекст.

Как сохранить цепочку исключений (причина)?

Конструкция raise ... from связывает новое исключение с исходным:

try:
    result = risky_operation()
except ValueError as cause:
    raise RuntimeError("Операция не удалась") from cause

В итоге в traceback будет показана вся цепочка: RuntimeError, вызванное ValueError. Это упрощает отладку.

Проблема: если указать from None, цепочка подавляется. Это может скрыть важную информацию. Решение - использовать from только когда это необходимо.

Как создать и использовать собственное исключение?

Пользовательские исключения наследуются от Exception (или его подклассов):

class MyCustomError(Exception):
    pass

raise MyCustomError("Ошибка в бизнес-логике")

Это помогает структурировать типы ошибок в проекте и обрабатывать их выборочно.

Ошибка: наследование от BaseException (вместо Exception) может перехватывать системные события (например, SystemExit). Решение - всегда наследовать от Exception.

Как передать уже созданный экземпляр исключения?

Можно вызвать raise с готовым объектом:

err = ValueError("Неверный ввод")
raise err

Это удобно, когда исключение формируется заранее, например, в функции-валидаторе.

Проблема: изменение экземпляра после вызова raise не влияет на исключение. Решение - создавать новый экземпляр при каждом выбрасывании.

Пример
def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Деление на ноль")
    return a / b

try:
    result = divide(10, 0)
except ZeroDivisionError as e:
    print(f"Ошибка: {e}")
Ошибка: Деление на ноль

Пример
class ValidationError(Exception):
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"{field}: {message}")

def validate_age(age):
    if age < 0:
        raise ValidationError("age", "Возраст не может быть отрицательным")
    return age

try:
    validate_age(-5)
except ValidationError as ve:
    print(f"Ошибка в поле {ve.field}: {ve.message}")
Ошибка в поле age: Возраст не может быть отрицательным

Пример
def fetch_data(url):
    try:
        # имитация запроса
        if not url.startswith("https"):
            raise ConnectionError("Небезопасное соединение")
    except ConnectionError as orig:
        raise RuntimeError("Не удалось получить данные") from orig

try:
    fetch_data("http://example.com")
except RuntimeError as e:
    print(f"Конечная ошибка: {e}")
    print(f"Причина: {e.__cause__}")
Конечная ошибка: Не удалось получить данные
Причина: Небезопасное соединение

Пример
def safe_division(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        print("Произошло деление на ноль, логируем...")
        raise  # переброс

try:
    safe_division(5, 0)
except ZeroDivisionError:
    print("Ошибка обработана выше")
Произошло деление на ноль, логируем...
Ошибка обработана выше

Пример
class Resource:
    def __enter__(self):
        print("Ресурс открыт")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Ресурс закрыт")
        if exc_type is ValueError:
            print("Подавляем ValueError")
            return True  # подавление исключения
        return False

with Resource():
    raise ValueError("Ошибка в блоке with")
Ресурс открыт
Ресурс закрыт
Подавляем ValueError

Вызов ошибки (raise) в Python - comments

En
вызов ошибки python (python)