Пользовательские исключения
Contents
Пользовательские исключения#
Создание простейшего пользовательского исключения#
Чтобы создать пользовательское исключение, необходимо всего лишь унаследовать от класса Exception
. Для большинства целей этого достаточно и тело класса оставляют пустым.
class MyException(Exception):
pass
raise MyException("Моё первое пользовательское исключение.")
---------------------------------------------------------------------------
MyException Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_16112/91365735.py in <module>
2 pass
3
----> 4 raise MyException("Моё первое пользовательское исключение.")
MyException: Моё первое пользовательское исключение.
В примере создан типа исключений MyException
, а сразу после оно возбуждено, что привело к появлению сообщения об ошибке, т.к. оно не обработано.
Обрабатывать исключения типа MyException
можно, как и любые другие.
try:
raise MyException("Моё пользовательское исключение!")
except MyException as msg:
print(msg)
Моё пользовательское исключение!
Т.к. MyException
наследует от Exception
, то проверка на него тоже будет проходить.
try:
raise MyException("Моё пользовательское исключение!")
except Exception as msg:
print(f"Перехвачено какое-то исключение со следующим сообщением:\n{msg}")
Перехвачено какое-то исключение с следующим сообщением:
Моё пользовательское исключение!
Более сложный случай#
Исключения являются полноценными классами, что позволяет добавлять им атрибуты, объявлять методы и т.п. Это можно использовать, чтобы вместе с возбуждаемым исключением передавать больше информации, но обычно исключения все же оставляют простыми.
В качестве примера создадим исключение, которое запоминает время, когда оно было возбужденно.
from time import ctime
class MyException(Exception):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.time = ctime()
Теперь в обработчике событий можно получить доступ к атрибуту time
.
try:
raise MyException("Какое-то сообщение!")
except MyException as e:
print(f'Перехвачено исключение типа MyException со следующим сообщением:\n"{e}"')
print(f"Время возбуждения исключения: {e.time}.")
В качестве второго примера приведем класс исключений, который подсчитывает количество раз, в программе возбуждались исключения данного типа.
class CountingException(Exception):
times_raised = 0
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
CountingException.times_raised += 1
for i in range(3):
try:
raise CountingException("Подсчитывающее исключение возбужденно.")
except CountingException as msg:
print(msg)
print(f"Подсчитывающее исключение было возбужденно {CountingException.times_raised} раз!")
Подсчитывающее исключение возбужденно.
Подсчитывающее исключение возбужденно.
Подсчитывающее исключение возбужденно.
Подсчитывающее исключение было возбужденно 3 раз!
Пример#
В качестве примера рассмотрим исключение IndexError, которое в частности возбуждается при попытке обращения по индексу за пределы списка. Предположим, что мы хотим знать не только, что мы обратились за пределы массива, но также и в какую конкретно сторону мы промахнулись: ниже минимального допустимого индекса или выше максимального.
Для этого определим два пользовательских типа исключений LowIndexError
и HighIndexError
.
class LowIndexError(IndexError):
def __init__(self, index, size):
message = f"Lowest acceptable index is {-size}, but {index} was given."
super().__init__(message)
class HighIndexError(IndexError):
def __init__(self, index, size):
message = f"Highest acceptable index is {size - 1}, but {index} was given."
super().__init__(message)
Т.к. обе этих ошибки сигнализируют об ошибке, связанной с индексом, то логично наследовать от IndexError
. Конструктор перегружен таким образом, чтобы он принимал индекс и размер коллекции и формировал сообщение с упоминанием значений этих параметров.
Теперь объявим пользовательский класс MyList
, который инкапсулирует в себе список своих элементов и делегирует ему все свои методы, но переопределяем доступ к элементам списка (метод __getitem__
) таким образом, что он бросает исключение LowIndexError
, если индекс ниже минимального, исключение HighIndexError
если индекс выше максимального.
Note
Вообще говоря, такая перегрузка метода __getitem__
сломает использование срезов, но в целях простоты оставим срезы за скобками.
class MyList:
def __init__(self, *elements):
self._elements = list(elements)
def __getitem__(self, k):
n = len(self._elements)
if k < -n:
raise LowIndexError(k, n)
elif k < n:
return self._elements[k]
else:
raise HighIndexError(k, n)
def __getattr__(self, attr):
return getattr(self._elements, attr)
l = MyList(1, 2, 3)
Теперь обращение к элементу со слишком низким индексом вызовет одну ошибку.
l[-4]
---------------------------------------------------------------------------
LowIndexError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_16112/2775217345.py in <module>
----> 1 l[-4]
~\AppData\Local\Temp/ipykernel_16112/1588382805.py in __getitem__(self, k)
6 n = len(self._elements)
7 if k < -n:
----> 8 raise LowIndexError(k, n)
9 elif k < n:
10 return self._elements[k]
LowIndexError: Lowest acceptable index is -3, but -4 was given.
А обращение к элементу со слишком высоким индексом — другую.
l[3]
---------------------------------------------------------------------------
HighIndexError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_16112/219327365.py in <module>
----> 1 l[3]
~\AppData\Local\Temp/ipykernel_16112/1588382805.py in __getitem__(self, k)
10 return self._elements[k]
11 else:
---> 12 raise HighIndexError(k, n)
13
14 def __getattr__(self, attr):
HighIndexError: Highest acceptable index is 2, but 3 was given.
Обе из них можно обработать через IndexError
.
try:
l[-4]
except IndexError as msg:
print(msg)
Lowest acceptable index is -3, but -4 was given.