JavaRush /Курсы /Модуль 1: Python Core /Создание своего исключения

Создание своего исключения

Модуль 1: Python Core
10 уровень , 6 лекция
Открыта

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

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

Основы создания пользовательских исключений

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

Пример создания простого пользовательского исключения

Шаг 1: Определение пользовательского исключения


class MyCustomError(Exception):
    """Класс для пользовательского исключения."""
    pass
        

Шаг 2: Использование пользовательского исключения


def check_value(value):
    if value < 0:
        raise MyCustomError("Значение не должно быть меньше нуля")
            
try:
    check_value(-1)
except MyCustomError as e:
    print(f"Произошло пользовательское исключение: {e}")
            

Все очень просто. Главное, чтобы ваше исключение было унаследовано от класса Exception или одного из его потомков.

7.2 Создание исключения с дополнительными атрибутами

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

Пример:


class NegativeValueError( Exception ):
    """Класс для пользовательского исключения при отрицательном значении."""
    def __init__(self, value, message = "Значение не должно быть меньше нуля"):
        self.value = value
        self.message = message
        super().__init__(self.message)

    def __str__(self):
        return f'{self.message}: {self.value}'

Использование исключения с дополнительными атрибутами


def check_value(value):
    if value < 0:
        raise NegativeValueError(value)

try:
    check_value(-1)
except NegativeValueError as e:
    print(f"Произошло пользовательское исключение: {e}")

Наше исключение — это класс, который унаследован от класса Exception. Поэтому с ним можно делать всё то же, что и с любым другим классом: добавлять поля, методы, параметры конструктора и т.п.

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

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

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

Пример:


class ApplicationError(Exception):
    """Базовый класс для всех исключений приложения."""
    pass

class NegativeValueError(ApplicationError):
    """Класс для пользовательского исключения при отрицательном значении."""
    def __init__(self, value, message="Значение не должно быть меньше нуля"):
        self.value = value
        self.message = message
        super().__init__(self.message)

    def __str__(self):
        return f'{self.message}: {self.value}'

class ValueTooLargeError(ApplicationError):
    """Класс для пользовательского исключения при слишком большом значении."""
    def __init__(self, value, message="Значение слишком велико"):
        self.value = value
        self.message = message
        super().__init__(self.message)

    def __str__(self):
        return f'{self.message}: {self.value}'

Использование иерархии пользовательских исключений


def check_value(value):
    if value < 0:
        raise NegativeValueError(value)
    elif value > 100:
        raise ValueTooLargeError(value)

try:
    check_value(150)
except NegativeValueError as e:
    print(f"Произошло исключение: {e}")
except ValueTooLargeError as e:
    print(f"Произошло исключение: {e}")
except ApplicationError as e:
    print(f"Общее исключение приложения: {e}")

7.4 Порядок перехвата исключений

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

Например, код:


def check_value(value):
    if value < 0:
        raise NegativeValueError(value)
    elif value > 100:
        raise ValueTooLargeError(value)
    
try:
    check_value(150)
except ApplicationError as e: # захватит исключения типа ApplicationError и всех его потомков
    print(f"Общее исключение приложения: {e}")

В блоке except будут перехвачены исключения типа ApplicationError и всех его классов-потомков.

А так как все исключения являются потомками класса Exception, то такой код захватит вообще все исключения:


def check_value(value):
    if value < 0:
        raise NegativeValueError(value)
    elif value > 100:
        raise ValueTooLargeError(value)
    
try:
    check_value(150)
except Exception as e:
    print(f"Захват всех исключений: {e}")
except ApplicationError as e: # Этот код никогда не выполнится
    print(f"Общее исключение приложения: {e}")

Захват исключений в блоке except ApplicationError никогда не произойдёт, так как блок except Exception захватит вообще все исключения типа Exception и любых его потомков.

Решение

Поэтому захватывать исключения принято в порядке, обратном наследованию: чем ближе класс к классу Exception, тем он ниже.

Пример:


def check_value(value):
    if value < 0:
        raise NegativeValueError(value)
    elif value > 100:
        raise ValueTooLargeError(value)
        
try:
    check_value(150)
except NegativeValueError as e:
    print(f"Произошло исключение: {e}")
except ApplicationError as e:
    print(f"Общее исключение приложения: {e}")
except Exception as e:
    print(f"Захват всех исключений: {e}")

В этом примере блоки except расположены в порядке, который соответствует их иерархии наследования: сначала перехватываются более специфичные исключения, такие как NegativeValueError, затем более общие, такие как ApplicationError. Это позволяет правильно обрабатывать исключения и избегать ситуаций, когда более общий обработчик захватывает исключение до того, как его могли бы обработать более специализированные блоки except.

2
Задача
Модуль 1: Python Core, 10 уровень, 6 лекция
Недоступна
Пользовательское исключение
Пользовательское исключение
2
Задача
Модуль 1: Python Core, 10 уровень, 6 лекция
Недоступна
Иерархия пользовательских исключений
Иерархия пользовательских исключений
Комментарии (5)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Mr.Robot Уровень 21 Expert
17 марта 2025
Ну вот эта конструкция в примерах выше:

super.__init__(self.message)
сама приводит к исключению вида:

TypeError: descriptor '__init__' requires a 'super' object but received a 'str'
Что только не пробовал вставлять вместо self.message - ошибка сохранялась Так, короче, ничего и непонятно... (
Mr.Robot Уровень 21 Expert
17 марта 2025
Сам виноват ))) Должно быть:

super().__init__(self.message)
Все прошло как нужно. Но валидатор пропускает и с неправильным вариантом - вот это меня и насторожило )
Дмитрий Уровень 27
17 марта 2025
Это потому что __init__ требует вызова экземпляра класса, а не результат вызова встроенного метода __str__, который выдаёт строку с классом, при попытке прямого обращения к классу, примерно так же, как при использовании print(super). Ну короче да, скобки надо поставить после super.
Slevin Уровень 64
4 июля 2025
Валидатор пропустил и просто с названием класса нового исключения и кодом 'pass' в теле класса
UnknownReboot Уровень 30
19 февраля 2025
Очень интересно! Мы не знаем что это такое, если бы мы знали что это такое, но мы не знаем что это такое!