Как работать с исключениями и создавать собственные
Исключения обычно возникают, когда программа обнаруживает непредвиденную ошибку. Например, если вы разделите на ноль или передадите строку функции, которой требуется число.
Но что такое исключения? Это просто объекты, и все они являются подклассами общего Exception
class. Наиболее частые исключения:
ZeroDivisionError
, который увеличивается при делении на нольTypeError
, который возникает, когда вы передаете элемент неправильного типа (например, если вы пытаетесь суммировать список и числоIndexError
, который возникает, когда вы даете индекс, выходящий за пределы допустимого диапазона.
Вы можете найти полный список исключений в документации Python.
Работа с исключениями
Когда вы вызываете функцию, которая может вызвать исключение, вы всегда должны иметь дело с ней. В противном случае вы столкнетесь с неожиданными сбоями.
Для этого вы должны включить опасную строку кода в блок try / catch. Например, предположим, что мы пишем код, который принимает список чисел и распечатывает ввод каждого из них.
def getInverse(listOfNumbers): for num in listOfNumbers: print(1/num)
Это может показаться простым кодом, но что произойдет, если в списке будет ноль?
>>>getInverse([1,2,0,4,5]) 1.0 0.5 Traceback (most recent call last): File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 6, in <module> getInverse([1,2,0,4,5]) File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 3, in getInverse print(1/num) ZeroDivisionError: division by zero
Вычисление 1/0
подняло ZeroDivisionError
, с которым мы не справились. Чтобы перехватить исключение, напишите блок try / catch вокруг оператора печати:
def getInverse2(listOfNumbers): for num in listOfNumbers: try: print(1/num) except: print("infinite")
Теперь мы перехватим исключение и вместо этого выведем «бесконечность».
>>>getInverse2([1,2,0,4,5]) 1.0 0.5 infinite 0.25 0.2
Обратите внимание, что мы не указали, какое исключение нужно перехватывать: это означает, что любое исключение сделает вывод кода «бесконечным».
Например, давайте посмотрим, какое исключение возникает, если в списке передается строка:
>>>getInverse([1,2,"a",4,5]) 1.0 0.5 Traceback (most recent call last): File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 13, in <module> getInverse([1,2,"a",4,5]) File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 3, in getInverse print(1/num) TypeError: unsupported operand type(s) for /: 'int' and 'str'
В нашей второй функции это другое исключение по-прежнему будет рассматриваться как ZeroDivisionError
:
>>>getInverse2([1,2,"a",4,5]) 1.0 0.5 infinite 0.25 0.2
В большинстве случаев мы хотим работать с разными исключениями по-разному. К счастью, мы можем написать несколько блоков except и заставить каждый работать с определенной ошибкой:
def getInverse3(listOfNumbers): for num in listOfNumbers: try: print(1/num) except ZeroDivisionError as zde: print("infinite") except TypeError as te: print("Not a Number")
Теперь мы можем правильно разобраться с обоими исключениями:
>>>getInverse3([1,2,"a",4,0,5]) 1.0 0.5 Not a Number 0.25 infinite 0.2
Вызов исключения
Иногда вы хотите создать собственное исключение. Например, если функция получает ввод неправильного типа, вы можете поднять TypeError
. Давайте снова изменим наш calculateInverse
method. Так что он выдает TypeError
, если тип ввода не является списком.
Для этого нам просто нужно использовать ключевое слово raise
:
def getInverse4(listOfNumbers): if type(listOfNumbers) != list: raise TypeError() for num in listOfNumbers: try: print(1/num) except ZeroDivisionError as zde: print("infinite") except TypeError as te: print("Not a Number")
Теперь, если мы дадим ввод неправильного типа, мы получим это сообщение:
>>>getInverse4(50) Traceback (most recent call last): File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 33, in <module> getInverse4(50) File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 24, in getInverse4 raise TypeError() TypeError
Это говорит нам о том, что было возбуждено TypeError
exception, но не более того, о причине, по которой оно было поднято. Можем ли мы сделать сообщение более конкретным? Ответ - да. Когда мы создаем новое исключение, мы можем передать в качестве аргумента строку. Это сообщение, которое мы хотим показать при выводе этого исключения пользователю:
def getInverse5(listOfNumbers): if type(listOfNumbers) != list: msg = "Required input of type list" raise TypeError(msg) # Other code remains the same
Теперь посмотрим на результат:
getInverse5(50)Traceback (most recent call last): File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 45, in <module> getInverse5(50) File "c:\Users\matte\Desktop\Anaconda Project\CV Project\Temporanei\prova.py", line 36, in getInverse5 raise TypeError(msg) TypeError: Required input of type list
Это намного яснее!
Создать собственные исключения
До сих пор мы использовали только предопределенные исключения. Однако действительно легко создавать собственные ошибки. Помните, что исключения - это просто классы, наследуемые от класса Exception
. Таким образом, вы можете создать свой собственный с помощью этого простого кода:
class MyException(Exception): pass
Однако обычно вы должны определить общее исключение, а затем сделать все ваши пользовательские (конкретные) исключения его подклассами. Например:
class MyProgramError(Exception): pass class FirstError(MyProgramError): pass class SecondError(MyProgramError): Pass
Наконец, вы также можете дополнительно настроить свои исключения, добавив некоторые параметры и методы.
Чтобы увидеть, как это работает, предположим, что мы работаем над программой для моделирования автомобиля. Затем мы создаем исключение, которое должно вызываться всякий раз, когда пользователь наезжает на другую машину. Вероятно, мы хотим получить информацию о другом автомобиле, который был сбит, когда мы имеем дело с исключением. Для этого давайте добавим собственный __init__
method, который также принимает в качестве входных данных другую машину:
class SimulationError(Exception): pass class CarCrashError(SimulationError): def __init__(self, otherCar, msg=None): self.otherCar = otherCar super().__init__(msg)
Теперь в коде мы можем написать что-то вроде:
if hasCrushed(user, otherCar): raise CarCrashError(otherCar)
И при работе с исключением:
except CarCrashError as cce: otherCar = cce.otherCar # Dealing with the crash
Наконец, обратите внимание, что когда мы вызываем init суперкласса, мы передаем параметр msg. Это связано с тем, что Exception
class принимает один необязательный параметр - отображаемое сообщение. Рекомендуется всегда устанавливать для исключений сообщение по умолчанию. Таким образом, даже если исключение не получит конкретного сообщения при его возникновении, оно все равно покажет что-то полезное. Вот как это сделать:
class MyError(Exception): def __init__(self, msg =None): if msg is None: msg = “This is the default message” super().__init__(msg)
Бонус: создание абстрактных классов
Абстрактные классы - это классы, которые содержат нереализованные методы. Во многих языках для этого есть специальное ключевое слово, например, abstract в Java. В Python его нет, но мы все еще можем создавать абстрактные методы, используя NotImplementedError
exception.
Давайте посмотрим на пример:
class MyAbstractClass(): def implementedMethod(self): #Code for this method return True def abstractMethod(self): raise NotImplementedError()
Теперь, если мы вызовем абстрактный метод, не переопределяя его в подклассе, Python вызовет исключение (например, что происходит в Java с ключевым словом abstract).
Заключение
В этой статье мы увидели, как использовать исключения. Однако помните, что исключения всегда должны быть вашим последним ресурсом. Вы должны использовать их только в том случае, если вы не можете предотвратить ошибку другим способом. Это не просто другой способ вернуть значение.
Спасибо, что дочитали до конца! Надеюсь, эта статья была полезной.
Больше контента на plainenglish.io