Чем полезна обработка ошибок при помощи try-except

Исключения и ошибки – это нежелательные события в программе, которые могут привести к преждевременному завершению ее выполнения. В 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».

6 комментариев к “Чем полезна обработка ошибок при помощи try-except”

  1. Пингбэк: Как преобразовать строку в число с плавающей точкой - pythonturbo

  2. Пингбэк: Как скачать видео с YouTube при помощи Python

  3. Пингбэк: Введение в Python Celery

  4. Пингбэк: Парсинг при помощи Python и Selenium

  5. Пингбэк: Пользовательский ввод и его обработка в Python

  6. Пингбэк: Ключевое слово pass в Python

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *