Пользовательские исключения

Пользовательские исключения – это отличный способ улучшить ваши проекты и сделать их более удобными для других разработчиков! В этой статье я расскажу, как создавать и организовывать пользовательские исключения для своих проектов.

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

В Python есть множество встроенных исключений, таких как ValueError, TypeError, IndexError и много других, но все они очень "общие". Часто бывает гораздо лучше создать специализированные исключения для конкретных сценариев в ваших проектах. Например, допустим, вы создаете бота для социальных сетей, который публикует посты в Telegram.

Нет встроенного исключения, которое точно описывало бы ошибку, такую как невозможность нажать кнопку «Опубликовать пост» или ошибку, связанную с тем, что Telegram не работает.

Создание простейшего пользовательского исключения

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

Для этого нам нужно создать класс с описательным именем, унаследовать его от класса Exception и добавить либо оператор pass, либо строку документации, объясняющую исключение. Не добавляя никакого кода в наше пользовательское исключение, мы позволяем встроенному классу Exception обрабатывать всю функциональность, что означает, что мы можем вызывать и перехватывать наше пользовательское исключение, как любое другое!

class CustomError(Exception):
    """Документация о том, для чего предназначено это пользовательское исключение."""

Вызываем и перехватываем пользовательские исключения

Поскольку мы наследуем от класса Exception, мы можем вызывать наше пользовательское исключение с помощью слова raise, и перехватывать с помощью блока try - except, как любое стандартное. Просто убедитесь, что оно импортировано в файл, который вы используете!

try:
    raise CustomError('Сообщение здесь')
except CustomError as e:
    print(f'Ошибка: {e}')

Вывод:

Ошибка: Сообщение здесь

Добавление сообщения по умолчанию к вашим исключениям

При вызове встроенных исключений в Python вы можете добавить сообщение к вашему исключению, но кто не забывает добавлять уникальное сообщение каждый раз?

При создании пользовательских исключений вы можете добавить всевозможные настраиваемые переменные, включая сообщение об исключении по умолчанию! В приведенном ниже примере, если пользователь пользовательского исключения не добавляет уникальное сообщение, используется сообщение по умолчанию.

class CustomError(Exception):
    default_message = 'Пользовательское исключение для класса ___'

    def __init__(self, message=None):
        super().__init__(message or self.default_message)

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

В Python исключения структурированы с использованием иерархий классов. При создании пользовательских исключений важно наследовать от соответствующего класса исключения для поддержания ясности и правильности.

Полная иерархия исключений описана здесь.

Как видно, все исключения наследуются от BaseException. Однако не следует наследовать Ваши собственные исключения от BaseException, поскольку KeyboardInterrupt и SystemExit являются подклассами BaseException.

Предположим, вы наследуете от BaseException и используете блоки try - except для перехвата вашего пользовательского исключения. В этом случае Python может непреднамеренно обработать ваше пользовательское исключение, когда вы на самом деле пытаетесь остановить код, используя Ctrl+C (что вызывает KeyboardInterrupt) или инициировать SystemExit. Именно поэтому обычно лучше наследовать от класса Exception, который является подклассом BaseException.

Для больших проектов с множеством исключений часто имеет смысл создать собственный родительский класс исключений.

Например, допустим, вы создаете какого-то бота; у вас может быть 25 различных типов исключений, связанных с кликами по разным частям веб-сайта. Поэтому логично сначала создать класс WebsiteClickException, который наследуется от Exception, а затем все ваши исключения, связанные с кликами, наследуют от WebsiteClickException. Преимущество этого подхода в том, что вы или люди, использующие ваш код, могут выбирать, обрабатывать ли конкретные исключения по кликам определенными способами или использовать общий обработчик для всех типов Ваших исключений.

class ClickException(Exception):
    """Базовое исключение для всех ошибок, связанных с кликами."""
    default_message = 'Произошла ошибка, связанная с кликом.'

    def __init__(self, message=None):
        super().__init__(message or self.default_message)

class ElementNotFoundException(ClickException):
    """Вызывается, когда элемент для клика не найден."""

class ElementNotClickableException(ClickException):
    """Вызывается, когда элемент присутствует, но не кликабелен."""

class PageNotLoadedException(ClickException):
    """Вызывается, когда страница не загрузилась должным образом."""

Пример использования:

try:
    # Симулированная логика: предположим, что мы не смогли найти элемент
    raise ElementNotFoundException()

except ElementNotClickableException as e:
    # Обработка ElementNotClickableException по-другому
    print(f"Ошибка ElementNotClickableException: {e}")

except ClickException as e:
    # Перехват всех остальных исключений по кликам
    print(f"Ошибка: {e}")

Вывод:

Ошибка: Произошла ошибка, связанная с кликом.

Организация множества пользовательских исключений

Если вы создаете одно или два пользовательских исключения, имеет смысл поместить классы исключений в тот же файл, что и сам модуль, для которого они созданы. Однако, когда у вас много пользовательских исключений, я рекомендую поместить их в файл под названием exceptions.py, чтобы оставаться организованным и поддерживать код, который легко импортировать в другие модули.

Лучшие практики и заключение

Несколько советов, о которых стоит подумать при создании своих собственных исключений:

  1. Значимые названия: Всегда называйте ваши исключения так, чтобы они ясно передавали их назначение. Обычно хорошие названия оканчиваются на Error или Exception
  2. Документация: Всегда документируйте ваши пользовательские исключения, объясняя сценарии, в которых они могут быть вызваны
  3. Избегайте чрезмерного использования: Не создавайте пользовательские исключения для каждой возможной ошибки. Создавайте их только тогда, когда они обеспечивают большую ясность или обрабатывают конкретные сценарии, которые встроенные исключения не могут эффективно покрыть

Пользовательские исключения предлагают улучшенный способ представления ошибок в ваших программах на Python. Они не только делают ваш код более читабельным, но и позволяют более детально обрабатывать ошибки.