Исключения в Python — примеры обработки
В этом уроке вы узнаете об исключениях Python и о том, как корректно их обрабатывать в программах.
Что такое исключения в Python?
В Python исключения — это объекты классов исключений. Все классы исключений являются подклассами класса BaseException.
Однако почти все встроенные классы исключений наследуются от класса Exception, который является подклассом класса BaseException:
На этой странице показана полная иерархия классов для встроенных исключений в Python.
В следующем примере определяется список из трех элементов и делается попытка доступа к четвертому:
colors = ['red', 'green', 'blue'] print(colors[3])
Недопустимый индекс вызвал, как и ожидалось, исключение IndexError:
IndexError: list index out of range
При возникновении исключения Python останавливает программу, пока вы его не обработаете. Чтобы обработать исключение, используйте оператор try…except. Например:
colors = ['red', 'green', 'blue'] try: print(colors[3]) except IndexError as e: print(e) print('Continue to run')
Выход:
<class 'IndexError'> - list index out of range Continue to run
В этом примере мы используем try…except для обработки исключения IndexError. Как видно из вывода, программа после этого продолжает работать.
Класс IndexError наследуется от класса LookupError, который в свою очередь наследуется от класса Exception:
И вы можете перехватить класс LookupError или Exception, когда возникает исключение IndexError. Например:
colors = ['red', 'green', 'blue'] try: print(colors[3]) except LookupError as e: print(e.__class__, '-', e) print('Continue to run')
Выход:
<class 'IndexError'> - list index out of range Continue to run
В этом примере исключением по-прежнему является IndexError, даже несмотря на то, что мы перехватываем исключение LookupError. Таким образом, когда вы обрабатываете исключение, обработчик исключений перехватывает указанный вами тип исключения и любой из его подклассов.
Программа работает так же, если вы используете класс Exception вместо класса LookupError:
colors = ['red', 'green', 'blue'] try: print(colors[3]) except Exception as e: print(e.__class__, '-', e) print('Continue to run')
Выход:
<class 'IndexError'> - list index out of range Continue to run
На практике вам следует перехватывать исключения как можно более конкретно, чтобы знать, как обращаться с каждым исключением определенным образом.
Пример обработки исключений в Python
В следующем примере определяется функция division, которая возвращает результат деления a на b:
def division(a, b): return a / b c = division(10, 0)
Выход:
ZeroDivisionError: division by zero
В этом примере, если b равно нулю, возникнет исключение ZeroDivisionError. Чтобы обработать исключение ZeroDivisionError, используйте оператор try…Exception следующим образом:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except ZeroDivisionError as e: return { 'success': False, 'message': 'b cannot be zero', 'result': None } result = division(10, 0) print(result)
В этом примере функция возвращает словарь, состоящий из трех элементов:
- success — это логическое значение, которое указывает, успешна операция или нет.
- message указывает на сообщение об ошибке или успехе.
- result сохраняет результат a/b или None, если b равно нулю.
Ниже показан вывод в случае возникновения ошибки ZeroDivisionError:
{'success': False, 'message': 'b cannot be zero', 'result': None}
Теперь, если вы не улавливаете исключение ZeroDivisionError, а более общее исключение, например класс Exception:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except Exception as e: return { 'success': False, 'message': 'b cannot be zero', 'result': None } result = division(10, 0) print(result)
Программа работает так же, как и раньше, потому что try…except также перехватывает тип исключения, который является подклассом класса Exception.
Однако если вы передадите в функцию division() две строки вместо двух чисел, вы получите то же сообщение, как если бы произошло исключение ZeroDivisionError:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except Exception as e: return { 'success': False, 'message': 'b cannot be zero', 'result': None } result = division('10', '2') print(result)
Выход:
{'success': False, 'message': 'b cannot be zero', 'result': None}
В этом примере исключением является не ZeroDivisionError, а TypeError. Однако код по-прежнему обрабатывает его как исключение ZeroDivisionError.
Поэтому вы всегда должны обрабатывать исключения от наиболее конкретных к наименее конкретным. Например:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except TypeError as e: return { 'success': False, 'message': 'Both a & b must be numbers', 'result': None } except ZeroDivisionError as e: return { 'success': False, 'message': 'b cannot be zero', 'result': None } except Exception as e: return { 'success': False, 'message': str(e), 'result': None } result = division('10', '2') print(result)
В этом примере мы перехватываем TypeError, ZeroDivisionError и Exception в том порядке, в котором они появляются в операторе try…except.
Если код, обрабатывающий разные исключения, один и тот же, вы можете сгруппировать все исключения в одно следующим образом:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except(TypeError, ZeroDivisionError, Exception) as e: return { 'success': False, 'message': str(e), 'result': None } result = division(10, 0) print(result)
Выход:
{'success': False, 'message': 'division by zero', 'result': None}