Исключения и ошибки – это нежелательные события в программе, которые могут привести к преждевременному завершению ее выполнения. В Python мы выполняем обработку исключений с помощью блоков try-except и finally. Зачем вообще обрабатывать исключения? В этой статье мы на примерах рассмотрим несколько причин.
Обработка ошибок, которые программист не может предсказать
В реальных приложениях существует множество случаев использования и ограничений на переменные, используемые в программах. Программист не может всегда проверять все ограничения. А если он пропустит некоторые случаи, это может привести к сбою в программе во время выполнения. Обработка ошибок при помощи try-except поможет справиться с неожиданными ошибками и восстановить программу, если это необходимо.
Предположим, пользователь нажимает клавиши Ctrl+c
или del
, чтобы прервать выполнение программы, после чего программа резко завершается из-за ошибки KeyBoardInterrupt. Мы можем обработать эту ошибку, используя try-except. При этом можно показать пользовательское сообщение перед завершением программы или выполнить другие инструкции для сохранения состояния программы в файловой системе. Таким образом мы можем избежать потери данных в случае, если пользователь ненароком нажмет клавиши, прерывающие работу программы.
try: dividend=int(input()) divisor=int(input()) print("Dividend is:",end=" ") print(dividend) print("Divisor is:",end=" ") print(divisor) quotient=dividend/divisor print("Quotient is:",end=" ") print(quotient) except (KeyboardInterrupt): print("Operation has been cancelled by the user")
Обработка исключений, возникающих во время выполнения
Даже если программа синтаксически корректна и обрабатывает все ограничения, во время выполнения может возникнуть ошибка, не зависящая от логики, реализованной в программе. Например, если файл не найден во время операции чтения файла, программа может столкнуться с ошибкой и преждевременно завершить выполнение. Но аварийного завершения можно избежать благодаря обработке ошибок.
Вы можете обработать и другие типы исключений, возникающих при выполнении программы: ZeroDivisionError, KeyError, NameError, IndexError и т.д.
Например, следующая программа предназначена для деления одного числа на другое. Но если делитель равен нулю, возникает ошибка ZeroDivisionError, и программа завершается:
dividend=10 divisor=0 print("Dividend is:",end=" ") print(dividend) print("Divisor is:",end=" ") print(divisor) quotient=dividend/divisor print("Quotient is:",end=" ") print(quotient)
Вывод:
Dividend is: 10 Divisor is: 0 Traceback (most recent call last): File "", line 1, in runfile('/home/aditya1117/untitled0.py', wdir='/home/aditya1117') File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 827, in runfile execfile(filename, namespace) File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 110, in execfile exec(compile(f.read(), filename, 'exec'), namespace) File "/home/aditya1117/untitled0.py", line 9, in quotient=dividend/divisor ZeroDivisionError: division by zero
Мы можем избежать преждевременного завершения программы, заключив операцию деления в блок try-except, чтобы при возникновении ошибки ZeroDivisionError она автоматически обрабатывалась кодом в блоке except:
dividend=10 divisor=0 print("Dividend is:",end=" ") print(dividend) print("Divisor is:",end=" ") print(divisor) try: quotient=dividend/divisor print("Quotient is:",end=" ") print(quotient) except (ZeroDivisionError): print("Divisor Cannot be zero")
Вывод:
Dividend is: 10 Divisor is: 0 Divisor Cannot be zero
Отделение обработки ошибок от бизнес-логики
Используя обработку ошибок try-except, мы можем легко отделить код, который обрабатывает ошибки, от кода, который реализует логику. Благодаря такому разделению код становится более читабельным.
Допустим, мы хотим определить год рождения человека и должны убедиться, что возраст не будет отрицательным. Мы сделаем это с помощью условных операторов if-else
:
age= -10 print("Age is:") print(age) if age<0: print("Input Correct age.") else: yearOfBirth= 2021-age print("Year of Birth is:") print(yearOfBirth)
Вывод:
Age is: -10 Input Correct age.
В приведенной выше программе мы проверяем и обрабатываем случай, когда возраст отрицательный, в одном и том же блоке кода. Но мы можем вынести проверку условия в один блок, а обработку ошибки – в другой, используя try-except:
try: age= -10 print("Age is:") print(age) if age<0: raise ValueError yearOfBirth= 2021-age print("Year of Birth is:") print(yearOfBirth) except ValueError: print("Input Correct age.")
Вывод:
Age is: -10 Input Correct age.
Здесь мы видим, что в блоке try
мы проверили неотрицательность возраста, и при отрицательном значении возраста возникает ValueError. Затем ValueError обрабатывается кодом в блоке except
для вывода сообщения. Таким образом мы разнесли код проверки условий и код обработки ошибок по разным блокам, что приведет к лучшей читаемости исходного кода.
Распространение ошибок вверх по стеку вызовов
В Python, если функция вызывается другой функцией, она может передавать ошибки вызывающей функции так же, как вызываемая функция возвращает любое значение. Этот механизм можно использовать для распространения ошибок на любое количество функций в стеке функций. Это дает нам возможность реализовать весь код обработки исключений в одном месте, а не в каждой функции.
Таким образом, мы можем обрабатывать все ошибки с помощью try-except в самой главной функции и вызывать исключения или ошибки в вызываемых функциях. Когда в любой из вызываемых функций будет возникать исключение, оно будет передано в главную функцию, где и будет обработано с помощью соответствующего кода.
Например, в приведенном ниже коде ошибка ValueError возникает в функции a()
, но код для обработки ValueError написан в функции b()
. Когда функция b()
вызывает функцию a()
, a()
передает ошибку в функцию b()
, после чего ошибка обрабатывается и выводится сообщение.
def a(): print("PythonForBeginners in function a") raise ValueError def b(): try: print("PythonForBeginners in function b") a() except Exception as e: print("Caught error from called function in function b") #call function b b()
Вывод:
PythonForBeginners in function b PythonForBeginners in function a Caught error from called function in function b
Когда в какой-либо функции возникает исключение, которое не обрабатывается в самой функции, интерпретатор Python просматривает стек вызовов функции назад, чтобы проверить, был ли код обработки такой ошибки реализован в какой-либо вызывающей функции. Если текущая функция на каком-либо этапе не имеет соответствующего кода обработки ошибки, ошибка передается в вызывающую функцию, пока не будет найден соответствующий код обработки ошибки или пока мы не достигнем главной функции.
Заключение
В этой статье мы рассмотрели, какие задачи решаются при помощи обработки ошибок с использованием try-except. Мы также вкратце прошлись по теме распространения ошибок в стеке вызовов функций и разобрали, чем это может быть полезно для написания модульного кода.
Перевод статьи Aditya Raj «Why try-except error handling is useful in Python».
Пингбэк: Как преобразовать строку в число с плавающей точкой - pythonturbo
Пингбэк: Как скачать видео с YouTube при помощи Python
Пингбэк: Введение в Python Celery
Пингбэк: Парсинг при помощи Python и Selenium
Пингбэк: Пользовательский ввод и его обработка в Python
Пингбэк: Ключевое слово pass в Python