Исключения в Python: классы ошибок и их наследование

Раздел: Python -> Исключения и обработка ошибок

Основы классов исключений в Python

В Python все исключения представлены классами, образующими иерархию. Корнем является BaseException. От него наследуются SystemExit, KeyboardInterrupt, GeneratorExit и Exception. Большинство исключений, с которыми работают разработчики, наследуются от Exception. Правильная организация перехвата исключений основана на порядке следования блоков except: от более специфичных к более общим.

try:
    number = int(input("Введите число: "))
    result = 10 / number
except ValueError:
    print("Введено не число")
except ZeroDivisionError:
    print("Деление на ноль")
except Exception as e:
    print(f"Неизвестная ошибка: {e}")

Python создать ошибку (создание исключений в python)

Цель:

обеспечить корректную обработку разных типов ошибок, не перекрывая специфичные обработчики общими.

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

Для создания пользовательского исключения достаточно определить класс, наследующий от Exception (или его подкласса). Это позволяет идентифицировать специфические ошибки в коде.

class InsufficientFundsError(Exception):
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError("Недостаточно средств")
    return balance - amount

try:
    new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
    print(e)

класс ошибок python (классы исключений в python)

Типичные проблемы:

  • Забыли наследовать от Exception. Если класс наследует от object, он не будет исключением и не будет перехвачен блоком except.
  • Использование слишком общего исключения в качестве базового для всех ошибок приложения - затрудняет тонкую обработку.

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

Через переопределение __init__ и __str__ можно передавать структурированные данные.

class HTTPRequestError(Exception):
    def __init__(self, status_code, message):
        self.status_code = status_code
        self.message = message
        super().__init__(f"HTTP {status_code}: {message}")

try:
    raise HTTPRequestError(404, "Not Found")
except HTTPRequestError as e:
    print(f"Код: {e.status_code}, Сообщение: {e.message}")

Python except (обработка исключений в python с помощью try-except)

Проблема:

забыть вызвать super().__init__() - тогда при преобразовании в строку (например, при логировании) может отображаться пустое сообщение.

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

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

def parse_int(s):
    try:
        return int(s)
    except ValueError as e:
        raise ValueError("Невозможно преобразовать строку в число") from e

try:
    parse_int("abc")
except ValueError as e:
    print("Исключение:", e)
    print("Причина:", e.__cause__)

Типичная ошибка:

использование from None подавляет цепочку, теряя контекст. Это следует делать осознанно, когда исходная ошибка не должна быть видна.

Как обрабатывать несколько исключений одновременно (ExceptionGroup)?

Начиная с Python 3.11, можно использовать ExceptionGroup для группировки исключений и перехвата их с помощью except*.

try:
    raise ExceptionGroup("Ошибки валидации",
                         [ValueError("поле name пустое"),
                          TypeError("поле age не число")])
except* ValueError as eg:
    print("Перехвачены ValueError:", eg.exceptions)
except* TypeError as eg:
    print("Перехвачены TypeError:", eg.exceptions)

Ограничение:

ExceptionGroup доступен только в Python 3.11+. Для более старых версий потребуется собственная реализация группового хранения исключений.

Расширенные примеры работы с классами исключений

Пример 1: Иерархия исключений для библиотеки

Пример
class BaseLibraryError(Exception):
    """Базовое исключение библиотеки"""
    pass

class NetworkError(BaseLibraryError):
    """Ошибка сети"""
    def __init__(self, url, status_code, message=""):
        self.url = url
        self.status_code = status_code
        super().__init__(f"Network error {status_code} at {url}: {message}")

class AuthError(BaseLibraryError):
    """Ошибка аутентификации"""
    def __init__(self, user, message="Authentication failed"):
        self.user = user
        super().__init__(f"Auth error for {user}: {message}")

try:
    raise NetworkError("https://api.example.com", 401, "Unauthorized")
except BaseLibraryError as e:
    print(type(e).__name__, ":", e)
NetworkError : Network error 401 at https://api.example.com: Unauthorized

Пример 2: Исключение со словарём ошибок и методом to_dict

Пример
class ValidationError(Exception):
    def __init__(self, errors: dict):
        self.errors = errors
        msg = "; ".join(f"{field}: {err}" for field, err in errors.items())
        super().__init__(msg)

    def to_dict(self):
        return self.errors

try:
    raise ValidationError({"email": "invalid format", "age": "must be integer"})
except ValidationError as e:
    print("Строковое представление:", str(e))
    print("Словарь ошибок:", e.to_dict())
Строковое представление: email: invalid format; age: must be integer
Словарь ошибок: {'email': 'invalid format', 'age': 'must be integer'}

Пример 3: Цепочка исключений с __cause__ и трассировка

Пример
import traceback

class DatabaseError(Exception):
    def __init__(self, query, original_exception):
        self.query = query
        self.original = original_exception
        super().__init__(f"Database error on query '{query}': {original_exception}")

def execute_query(query):
    try:
        raise ConnectionError("Cannot connect to database")
    except ConnectionError as ce:
        raise DatabaseError(query, ce) from ce

try:
    execute_query("SELECT * FROM users")
except DatabaseError as de:
    print("Трассировка:", traceback.format_exception_only(type(de), de))
    print("Цепочка: __cause__:", de.__cause__)
Трассировка: ['DatabaseError: Database error on query \'SELECT * FROM users\': Cannot connect to database\n']
Цепочка: __cause__: Cannot connect to database

Классы исключений в Python - comments

En
класс ошибок python (python)