Работа с исключениями в ООП: пользовательские типы ошибок
Зачем нужны пользовательские исключения? 🏗️
Когда стандартных исключений Python (ValueError, TypeError и др.) недостаточно, мы создаём свои! Это делает код:
- Яснее — ошибки говорят на языке вашей предметной области
- Контролируемее — вы решаете, какие данные передавать с ошибкой
- Безопаснее — исключения можно группировать в иерархии
Пример из реального мира: банковское приложение. Ошибка InsufficientFundsError понятнее общего ValueError.
Создаём первое пользовательское исключение 🐣
Любое пользовательское исключение — это класс, унаследованный от Exception:
class MyCustomError(Exception):
"""Моё первое исключение!"""
pass
Используем его:
def risky_operation():
raise MyCustomError("Что-то пошло не так!")
try:
risky_operation()
except MyCustomError as e:
print(f"Поймали: {e}")
Добавляем логику в исключения ⚡
Исключения могут содержать методы и атрибуты, как обычные классы:
class TemperatureError(Exception):
def __init__(self, temp, min_temp, max_temp):
self.temp = temp
self.min_temp = min_temp
self.max_temp = max_temp
message = f"{temp}°C не в диапазоне {min_temp}-{max_temp}°C"
super().__init__(message)
def set_oven_temp(temp):
if not 100 <= temp <= 250:
raise TemperatureError(temp, 100, 250)
print(f"Печь нагрета до {temp}°C")
try:
set_oven_temp(300)
except TemperatureError as e:
print(f"Ошибка печи: {e}. Переданная температура: {e.temp}°C")
Иерархия исключений в ООП 🌳
Создаём целое семейство ошибок для сложных систем:
class DatabaseError(Exception):
"""Базовое исключение для всех ошибок БД"""
class ConnectionError(DatabaseError):
"""Проблемы с подключением"""
class QueryError(DatabaseError):
"""Ошибки в SQL-запросе"""
def execute_query(query):
if not query.startswith("SELECT"):
raise QueryError(f"Неподдерживаемый запрос: {query}")
print("Запрос выполнен!")
try:
execute_query("DELETE * FROM users")
except DatabaseError as e:
print(f"Ошибка базы данных: {type(e).__name__} - {e}")
Практика: система аутентификации 🔐
Создадим безопасную систему с понятными ошибками:
class AuthError(Exception):
pass
class WeakPasswordError(AuthError):
def __init__(self, password):
self.password = password
strength = self._check_strength()
super().__init__(f"Пароль слишком {strength}")
def _check_strength(self):
if len(self.password) < 6:
return "короткий"
return "простой"
def register_user(username, password):
if len(password) < 8:
raise WeakPasswordError(password)
print(f"Пользователь {username} зарегистрирован!")
try:
register_user("admin", "123")
except WeakPasswordError as e:
print(f"Ошибка регистрации: {e}") # "Ошибка регистрации: Пароль слишком короткий"
Продвинутые техники 🚀
1. Модульные исключения — выносите ошибки в отдельный файл errors.py
2. Логирование — записывайте кастомные ошибки в лог:
import logging
logging.basicConfig(filename='app.log')
try:
risky_call()
except CustomError as e:
logging.error(f"Custom error occurred: {e}", exc_info=True)
3. Сериализация — передавайте исключения между процессами:
import pickle
error = pickle.dumps(MyError("Описание"))
restored_error = pickle.loads(error)
Главные правила пользовательских исключений 📜
- Ясность имён —
PaymentFailedErrorлучше чемTransactionException - Информативность — добавляйте полезные атрибуты для отладки
- Иерархия — стройте логические цепочки наследования
- Документируйте — пишите docstring для каждого класса ошибок
Как говорит Данила Бежин в своих видео: "Хорошие исключения — это не просто ошибки, это часть API вашей программы!" Смотрите его разборы на YouTube-канале.