Собственные классы исключений python

Инструкция raise и пользовательские исключения

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

print("Куда ты скачешь, гордый конь,") print("И где опустишь ты копыта?") print("О мощный властелин судьбы!") 1/0 print("Не так ли ты над самой бездной") print("На высоте, уздой железной") print("Россию поднял на дыбы?")

Но как эта операция деления формирует само исключение? Для этого в языке Python имеется конструкция (оператор)

которая и порождает указанные типы исключений. В самом простом варианте, мы можем вместо деления на ноль записать этот оператор и указать тип исключения ZeroDivisionError:

raise ZeroDivisionError("Деление на ноль")

Результат выполнения программы будет тем же – она остановится на конструкции raise. Только сообщение об ошибке теперь будет на русском языке – та строка, что мы указали при формировании объекта класса ZeroDivisionError. То есть, после оператора raise мы можем прописывать нужный нам класс исключения с собственными параметрами. Также можно просто указывать класс, не прописывая каких-либо параметров:

Здесь у нас также создается экземпляр, но без параметров. Раз это так, значит, можно заранее создать экземпляр класса:

e = ZeroDivisionError("Деление на ноль")

а, затем, сгенерировать это исключение:

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

Например, если просто указать строку после оператора raise:

то интерпретатор Python как раз это нам и укажет:

TypeError: exceptions must derive from BaseException

То есть, после raise должен находиться экземпляр класса исключения, а не какой-то произвольный объект.

Когда нам может понадобиться оператор raise? И разве сам язык Python не может генерировать нужные исключения при возникновении ошибок? Часто именно так и происходит. Например, если мы будем делать некорректные операции, вроде:

то автоматически возникают ошибки заданного типа. Но прописать исключения на все случаи жизни невозможно. И если в качестве примера взять все тот же класс печати данных:

То, в частности, метод send_data() может генерировать свое исключение, если по каким-то причинам данные не были отправлены принтеру. В качестве демонстрации я приведу гипотетический класс PrintData для работы с принтером:

class PrintData: def print(self, data): self.send_data(data) print(f"печать: ") def send_data(self, data): if not self.send_to_print(data): raise Exception("принтер не отвечает") def send_to_print(self, data): return False

Как раз здесь мы генерируем исключение, если данные не могут быть отправлены принтеру. Затем, это исключение может быть обработано на любом уровне стека вызова. Например, если далее создать экземпляр этого класса и вызвать метод print():

То мы увидим сформированное нами исключение. Как вы понимаете, в язык Python не встроена по умолчанию возможность генерации исключения при взаимодействии с принтером. Это приходится делать уже самому программисту с помощью оператора raise. Вот для этого он и нужен.

Читайте также:  Css table header position sticky

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

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

Остальные классы наследуются от него и имеют строгую специализацию, кроме, разве что, класса Exception, который является общим для большого разнообразия типов исключений в момент выполнения программы. Так почему же мы выбрали класс Exception, а не BaseException? Дело в том, что классы SystemExit, GeneratorExit и KeyboardInterrupt являются весьма специфичными и, обычно, они не используются при обработке собственных исключений. Поэтому, целесообразно выбирать именно класс Exception для формирования новых собственных классов исключений. Что мы сейчас и сделаем.

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

class ExceptionPrintSendData(Exception): """Класс исключения при отправке данных принтеру"""

И далее в программе использовать этот новый класс:

def send_data(self, data): if not self.send_to_print(data): raise ExceptionPrintSendData("принтер не отвечает")

Соответственно, ниже в программе, мы можем обработать этот тип ошибки, просто указав имя нашего нового класса:

p = PrintData() try: p.print("123") except ExceptionPrintSendData: print("Ошибка печати")

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

Разумеется, мы поймаем эту же ошибку, если укажем базовый класс Exception, но пропустим, если указать какой-либо другой независимый класс исключения, иерархически не связанный с нашим, например, ArithmeticError.

Кроме того, мы можем расширить функционал класса ExceptionPrintSendData. Давайте добавим в него конструктор. Он прописывается для произвольного числа аргументов:

class ExceptionPrintSendData(Exception): """Класс исключения при отправке данных принтеру""" def __init__(self, *args): self.message = args[0] if args else None

А также магически метод__str__ для представления ошибки в консоли:

def __str__(self): return f"Ошибка: "

Если теперь убрать блок try/except и вызвать метод print(), то увидим наш вариант отображения ошибки в консоли:

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

Наконец, пользовательские классы исключений дают возможность создавать свою иерархию исключений. В частности, в нашем примере, можно прописать общий класс исключений для принтера ExceptionPrint:

class ExceptionPrint(Exception): """Общий класс исключения принтера"""

А, затем, остальные, более конкретные типы наследовать от него:

class ExceptionPrintSendData(ExceptionPrint): """Класс исключения при отправке данных принтеру"""

В результате, в блоке except мы можем отлавливать как конкретные типы ошибок, так и общие, связанные с принтером:

p = PrintData() try: p.print("123") except ExceptionPrintSendData as e: print(e) except ExceptionPrint: print("Ошибка печати")

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

Читайте также:  Php архивация всех файлов

Видео по теме

Концепция ООП простыми словами

#1. Классы и объекты. Атрибуты классов и объектов

#2. Методы классов. Параметр self

#3. Инициализатор __init__ и финализатор __del__

#4. Магический метод __new__. Пример паттерна Singleton

#5. Методы класса (classmethod) и статические методы (staticmethod)

#6. Режимы доступа public, private, protected. Сеттеры и геттеры

#7. Магические методы __setattr__, __getattribute__, __getattr__ и __delattr__

#9. Свойства property. Декоратор @property

#10. Пример использования объектов property

#11. Дескрипторы (data descriptor и non-data descriptor)

#12. Магический метод __call__. Функторы и классы-декораторы

#13. Магические методы __str__, __repr__, __len__, __abs__

#14 Магические методы __add__, __sub__, __mul__, __truediv__

#15. Методы сравнений __eq__, __ne__, __lt__, __gt__ и другие

#16. Магические методы __eq__ и __hash__

#17. Магический метод __bool__ определения правдивости объектов

#18. Магические методы __getitem__, __setitem__ и __delitem__

#19. Магические методы __iter__ и __next__

#20. Наследование в объектно-ориентированном программировании

#21. Функция issubclass(). Наследование от встроенных типов и от object

#22. Наследование. Функция super() и делегирование

#23. Наследование. Атрибуты private и protected

#24. Полиморфизм и абстрактные методы

#25. Множественное наследование

#27. Как работает __slots__ с property и при наследовании

#28. Введение в обработку исключений. Блоки try / except

#29. Обработка исключений. Блоки finally и else

#30. Распространение исключений (propagation exceptions)

#31. Инструкция raise и пользовательские исключения

#32. Менеджеры контекстов. Оператор with

#34. Метаклассы. Объект type

#35. Пользовательские метаклассы. Параметр metaclass

#36. Метаклассы в API ORM Django

#37. Введение в Python Data Classes (часть 1)

#38. Введение в Python Data Classes (часть 2)

#39. Python Data Classes при наследовании

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

Источник

Собственные классы исключений python

Иногда возникает необходимость вручную сгенерировать то или иное исключение. Для этого применяется оператор raise . Например, сгенерируем исключение

try: number1 = int(input("Введите первое число: ")) number2 = int(input("Введите второе число: ")) if number2 == 0: raise Exception("Второе число не должно быть равно 0") print("Результат деления двух чисел:", number1/number2) except ValueError: print("Введены некорректные данные") except Exception as e: print(e) print("Завершение программы")

Оператору raise передается объект BaseException — в данном случае объект Exception. В конструктор этого типа можно ему передать сообщение, которое затем можно вывести пользователю. В итоге, если number2 будет равно 0, то сработает оператор raise, который сгенерирует исключение. В итоге управление программой перейдет к блоку except , который обрабатывает исключения типа Exception:

Введите первое число: 1 Введите второе число: 0 Второе число не должно быть равно 0 Завершение программы

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

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

class Person: def __init__(self, name, age): self.__name = name # устанавливаем имя self.__age = age # устанавливаем возраст def display_info(self): print(f"Имя: Возраст: ")

Здесь класс Person в конструкторе получает значения для имени и возраста и присваивает их приватным переменным name и age. Однако при создании объекта Person мы можем передать в конструктор некорректное с точки зрения логики значение — например, отрицательное число. Одним из способов решения данной ситуации представляет генерация исключения при передаче некорректных значений.

Читайте также:  Потоки в python это

Итак, определим следующий код программы:

class PersonAgeException(Exception): def __init__(self, age, minage, maxage): self.age = age self.minage = minage self.maxage = maxage def __str__(self): return f"Недопустимое значение: . " \ f"Возраст должен быть в диапазоне от до " class Person: def __init__(self, name, age): self.__name = name # устанавливаем имя minage, maxage = 1, 110 if minage < age < maxage: # устанавливаем возраст, если передано корректное значение self.__age = age else: # иначе генерируем исключение raise PersonAgeException(age, minage, maxage) def display_info(self): print(f"Имя: Возраст: ") try: tom = Person("Tom", 37) tom.display_info() # Имя: Tom Возраст: 37 bob = Person("Bob", -23) bob.display_info() except PersonAgeException as e: print(e) # Недопустимое значение: -23. Возраст должен быть в диапазоне от 1 до 110

В начале здесь определен класс PersonAgeException, который наследуется от класса Exception. Как правило, собственные классы исключений наследуются от класса Exception. Класс PersonAgeException предназначен для исключений, связанных с возрастом пользователя.

В конструкторе PersonAgeException получаем три значения - собственное некорректное значение, которое послужило причиной исключения, а также минимальное и максимальное значения возраста.

class PersonAgeException(Exception): def __init__(self, age, minage, maxage): self.age = age self.minage = minage self.maxage = maxage def __str__(self): return f"Недопустимое значение: . " \ f"Возраст должен быть в диапазоне от до "

В функции __str__ определяем текстовое представление класса - по сути сообщение об ошибке.

В конструкторе класса Persoon проверяем переданное для возраста пользователя значение. И если это значение не соответствует определенному диапазону, то генерируем исключение типа PersonAgeException:

raise PersonAgeException(age, minage, maxage)

При применении класса Person нам следует учитывать, что конструктор класса может сгенерировать исключение при передаче некорректного значения. Поэтому создание объектов Person обертывается в конструкцию try..except:

try: tom = Person("Tom", 37) tom.display_info() # Имя: Tom Возраст: 37 bob = Person("Bob", -23) # генерируется исключение типа PersonAgeException bob.display_info() except PersonAgeException as e: print(e) # Недопустимое значение: -23. Возраст должен быть в диапазоне от 1 до 110

И если при вызове конструктора Person будет сгенерировано исключение типа PersonAgeException, то управление программой перейдет к блоку except, который обрабатывает исключения типа PersonAgeException в виде вывода информации об исключении на консоль.

Источник

Оцените статью