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.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ