← Назад к вопросам

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

1.3 Junior🔥 211 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Создание пользовательских исключений в Python

Пользовательские исключения позволяют создавать специфичные для вашего приложения ошибки, делая код более читаемым и позволяя обработчикам правильно реагировать на разные типы ошибок.

Базовые пользовательские исключения

# Простое исключение
class CustomError(Exception):
    pass

# Использование
try:
    raise CustomError("Что-то пошло не так")
except CustomError as e:
    print(f"Ошибка: {e}")

Иерархия исключений

# Базовый класс для всех ошибок приложения
class ApplicationError(Exception):
    pass

# Специфичные исключения
class ValidationError(ApplicationError):
    pass

class DatabaseError(ApplicationError):
    pass

class AuthenticationError(ApplicationError):
    pass

class AuthorizationError(ApplicationError):
    pass

# Использование
try:
    if not email:
        raise ValidationError("Email обязателен")
    if not user_authenticated:
        raise AuthenticationError("Пользователь не авторизован")
except ValidationError as e:
    print(f"Ошибка валидации: {e}")
except AuthenticationError as e:
    print(f"Ошибка аутентификации: {e}")
except ApplicationError as e:
    print(f"Ошибка приложения: {e}")

Исключения с параметрами

class InvalidUserError(Exception):
    def __init__(self, user_id, message="Пользователь не найден"):
        self.user_id = user_id
        self.message = message
        super().__init__(self.message)
    
    def __str__(self):
        return f"Пользователь {self.user_id}: {self.message}"

# Использование
try:
    raise InvalidUserError(user_id=123, message="ID не валиден")
except InvalidUserError as e:
    print(f"Ошибка: {e}")
    print(f"ID пользователя: {e.user_id}")

Исключения с кодами ошибок

class APIError(Exception):
    def __init__(self, code, message, details=None):
        self.code = code
        self.message = message
        self.details = details or {}
        super().__init__(message)
    
    def to_dict(self):
        return {
            "error_code": self.code,
            "message": self.message,
            "details": self.details
        }

# Использование в FastAPI
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(APIError)
async def api_error_handler(request, exc):
    return JSONResponse(
        status_code=400,
        content=exc.to_dict()
    )

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    if user_id < 0:
        raise APIError(
            code="INVALID_ID",
            message="ID пользователя не может быть отрицательным",
            details={"provided_id": user_id}
        )

Исключения с контекстом

class DatabaseConnectionError(Exception):
    def __init__(self, host, port, original_error):
        self.host = host
        self.port = port
        self.original_error = original_error
        message = f"Не удалось подключиться к {host}:{port}"
        super().__init__(message)

# Использование с цепочкой исключений
try:
    try:
        connection = connect_to_db(host, port)
    except ConnectionRefusedError as e:
        raise DatabaseConnectionError(host, port, e) from e
except DatabaseConnectionError as e:
    print(f"Ошибка БД: {e}")
    print(f"Оригинальная ошибка: {e.original_error}")

Практические примеры в приложении

# Для приложения с пользователями
class UserNotFoundError(ApplicationError):
    def __init__(self, user_id):
        self.user_id = user_id
        super().__init__(f"Пользователь {user_id} не найден")

class DuplicateEmailError(ApplicationError):
    def __init__(self, email):
        self.email = email
        super().__init__(f"Email {email} уже зарегистрирован")

class InsufficientPermissionsError(ApplicationError):
    def __init__(self, user_id, required_role):
        self.user_id = user_id
        self.required_role = required_role
        super().__init__(f"Пользователь {user_id} не имеет роль {required_role}")

# Использование в сервисе
class UserService:
    def __init__(self, db):
        self.db = db
    
    def create_user(self, email, password):
        if self.db.user_exists(email):
            raise DuplicateEmailError(email)
        return self.db.create(email, password)
    
    def get_user(self, user_id):
        user = self.db.get(user_id)
        if not user:
            raise UserNotFoundError(user_id)
        return user
    
    def delete_user(self, user_id, current_user_id):
        if current_user_id != user_id:
            raise InsufficientPermissionsError(current_user_id, "admin")
        return self.db.delete(user_id)

Обработка исключений в Flask

from flask import Flask, jsonify

app = Flask(__name__)

class APIError(Exception):
    def __init__(self, message, status_code=400):
        self.message = message
        self.status_code = status_code

@app.errorhandler(APIError)
def handle_api_error(error):
    response = jsonify({"error": error.message})
    response.status_code = error.status_code
    return response

@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    if user_id < 1:
        raise APIError("ID должен быть положительным", status_code=400)
    # ... остальной код

Логирование исключений

import logging

logger = logging.getLogger(__name__)

class ApplicationError(Exception):
    def __init__(self, message, log_level=logging.ERROR):
        super().__init__(message)
        logger.log(log_level, f"Application error: {message}", exc_info=True)

class ValidationError(ApplicationError):
    def __init__(self, message):
        super().__init__(message, log_level=logging.WARNING)

# Использование
try:
    validate_email(email)
except ValidationError as e:
    # Логируется автоматически
    pass

Контекстные менеджеры для исключений

from contextlib import contextmanager

@contextmanager
def handle_database_error():
    try:
        yield
    except Exception as e:
        logger.error(f"Database error: {e}")
        raise DatabaseError(f"Ошибка БД: {str(e)}")

# Использование
with handle_database_error():
    result = db.query("SELECT * FROM users")

Лучшие практики

  • Наследуйте от Exception, не от BaseException
  • Создавайте иерархию исключений для вашего приложения
  • Используйте описательные имена (заканчивайте на "Error")
  • Сохраняйте контекст в атрибутах исключения
  • Не ловите все исключения (except Exception), ловите специфичные
  • Логируйте полную трассировку стека
  • Используйте raise from для цепочки исключений
  • Документируйте какие исключения может выбросить функция

Чек-лист

  • Иерархия исключений организована логично
  • Каждый класс имеет ясное назначение
  • Исключения содержат полезный контекст
  • Обработчики ловят специфичные исключения
  • Нет ловли всех исключений
  • Логирование настроено правильно
Как создать свои исключения? | PrepBro