raise и собственные исключения
Ключевое слово raise в Python и создание собственных классов исключений: когда и зачем генерировать ошибки вручную.
raise позволяет намеренно вызвать исключение. Это нужно для валидации данных, сигнализации об ошибочных состояниях и создания понятных API.
Базовое использование raise
def set_age(age):
if age < 0 or age > 150:
raise ValueError(f"Недопустимый возраст: {age}")
return age
print(set_age(25))
try:
print(set_age(-5))
except ValueError as e:
print(f"Ошибка: {e}")
Вывод:
25 Ошибка: Недопустимый возраст: -5
Повторное выбрасывание исключения
Иногда нужно обработать исключение частично (например, залогировать) и пробросить его дальше. Голый raise без аргументов повторно бросает текущее исключение:
def load_data(path):
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError as e:
print(f"[LOG] Файл не найден: {path}")
raise # пробрасываем исключение выше
try:
load_data("missing.txt")
except FileNotFoundError:
print("Обработано на верхнем уровне")
Вывод:
[LOG] Файл не найден: missing.txt Обработано на верхнем уровне
Собственные классы исключений
Создание своего класса исключения — просто наследование от Exception:
class InsufficientFundsError(Exception):
"""Ошибка: недостаточно средств на счёте."""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(
f"Нельзя снять {amount} руб. — на счёте только {balance} руб."
)
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
return self.balance
account = BankAccount(100)
try:
account.withdraw(150)
except InsufficientFundsError as e:
print(e)
print(f"Баланс: {e.balance}, запрошено: {e.amount}")
Вывод:
Нельзя снять 150 руб. — на счёте только 100 руб. Баланс: 100, запрошено: 150
Собственное исключение несёт дополнительные данные (баланс, запрошенная сумма), по которым вызывающий код может принять решение. Это главное преимущество кастомных классов перед голым
ValueError.
Иерархия собственных исключений
class AppError(Exception):
"""Базовый класс для ошибок приложения."""
class ValidationError(AppError):
"""Ошибка валидации данных."""
class DatabaseError(AppError):
"""Ошибка работы с базой данных."""
def process(data):
if not data:
raise ValidationError("Данные не могут быть пустыми")
try:
process(None)
except AppError as e:
# поймает ValidationError, DatabaseError и любой AppError
print(f"Ошибка приложения: {e}")
Вывод:
Ошибка приложения: Данные не могут быть пустыми
Коротко
raise ТипОшибки("сообщение")намеренно генерирует исключение.- Голый
raiseвнутриexceptпробрасывает текущее исключение выше. - Собственное исключение — класс, наследующий от
Exception; может хранить дополнительные данные. - Строите иерархию: базовый класс → конкретные ошибки. Это позволяет ловить группу через родителя.
Проверьте себя
1. Что делает голый raise без аргументов внутри блока except?
AСоздаёт новое пустое исключение
BПовторно бросает текущее перехваченное исключение
CИгнорирует исключение
DВызывает SystemExit
2. От какого класса наследуют при создании собственного исключения?
Aobject
BError
CException
DBaseException
3. Зачем создавать собственные классы исключений вместо использования ValueError?
AТолько для красивых имён
BЧтобы ускорить программу
CЧтобы хранить дополнительные данные и позволить точечный перехват
DСобственные исключения не рекомендуется создавать