Генерация исключений: raise
Генерация исключений: raise
Мы умеем перехватывать исключения. Теперь научимся их генерировать самостоятельно. raise позволяет сигнализировать об ошибках из собственного кода — сообщать вызывающему, что переданные данные некорректны или операция невозможна.
Зачем генерировать исключения
Представьте функцию set_age(age). Возраст не может быть отрицательным. Как сообщить об ошибке? Можно вернуть None, можно напечатать сообщение — но это заставит вызывающий код везде проверять возврат. Стандартный Python-способ — выбросить исключение:
def set_age(age):
if age < 0:
raise ValueError(f"Возраст не может быть отрицательным: {age}")
if age > 150:
raise ValueError(f"Неправдоподобный возраст: {age}")
return age
try:
set_age(-5)
except ValueError as e:
print(e) # Возраст не может быть отрицательным: -5
Синтаксис raise
raise ExceptionType("описание ошибки")
raise ExceptionType # без сообщения — допустимо, но менее информативно
raise # без аргументов — перебросить текущее исключение (в except)
Создаётся экземпляр исключения и «бросается» вверх по стеку вызовов, пока кто-то его не перехватит. Если никто не перехватит — программа завершится с трейсбеком.
Выбор типа исключения
Используйте подходящий встроенный тип — это помогает вызывающему коду понять, что пошло не так:
def connect(host, port):
if not isinstance(host, str):
raise TypeError(f"host должен быть строкой, получен {type(host).__name__}")
if not 1 <= port <= 65535:
raise ValueError(f"Порт должен быть от 1 до 65535, получен {port}")
# ... логика подключения
def load_user(user_id, users_db):
if user_id not in users_db:
raise KeyError(f"Пользователь {user_id} не найден")
return users_db[user_id]
Перебросить исключение: raise без аргументов
Иногда нужно поймать исключение, выполнить какое-то действие (логирование) и снова бросить его дальше:
def process(data):
try:
result = complex_operation(data)
except ValueError as e:
print(f"Логирование ошибки: {e}")
raise # перебросить то же исключение дальше
return result
Голый raise без аргументов повторно бросает текущее исключение, сохраняя его тип и трейсбек.
raise from: цепочка исключений
Когда одно исключение возникает вследствие другого, полезно сохранить контекст. raise X from Y явно указывает причинно-следственную связь:
def load_config(path):
try:
with open(path) as f:
import json
return json.load(f)
except json.JSONDecodeError as e:
raise ValueError(f"Невалидный конфиг в {path}") from e
При выводе Python покажет обе ошибки: оригинальную (JSONDecodeError) и новую (ValueError).
Практический пример: валидация данных
def create_user(name, age, email):
if not isinstance(name, str) or not name.strip():
raise ValueError("Имя не может быть пустым")
if not isinstance(age, int) or not 0 <= age <= 120:
raise ValueError(f"Некорректный возраст: {age}")
if "@" not in email:
raise ValueError(f"Некорректный email: {email}")
return {"name": name.strip(), "age": age, "email": email}
try:
user = create_user("Алиса", 25, "alice@example.com")
print(user)
except ValueError as e:
print(f"Ошибка создания пользователя: {e}")
Проверь себя
Что произойдёт, если вызвать raise вне блока except?
Python выбросит RuntimeError: No active exception to re-raise. Голый raise работает только внутри блока обработки исключений, чтобы перебросить активное исключение.
Итог
raise ExceptionType("message")— сгенерировать исключение с описанием.- Используйте встроенные типы:
ValueError(плохое значение),TypeError(плохой тип),KeyError(ключ не найден). raiseбез аргументов вexcept— перебросить текущее исключение.raise X from Y— указать причину (исходное исключение).- Исключения — стандартный Python-способ сигнализировать о некорректных данных.
В следующем уроке узнаем, как создавать собственные классы исключений для точной типизации ошибок в ваших библиотеках и приложениях.