Обработка исключений в Python

PythonBeginner
Практиковаться сейчас

Введение

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

В этой лабораторной работе вы изучите основы обработки исключений в Python. Вы начнете с базовых блоков try и except, перейдете к обработке нескольких исключений, разберетесь с использованием блока finally и, наконец, научитесь создавать и использовать собственные пользовательские исключения.

Базовая обработка исключений

На этом этапе вы изучите основы обработки исключений в Python с помощью блоков try и except.

Математически доказано, что деление на ноль невозможно. Если вы попытаетесь сделать это в Python, интерпретатор вызовет встроенное исключение ZeroDivisionError. Давайте посмотрим, как это работает.

Примечание: Если вы не знакомы с использованием WebIDE (похожей на VS Code), пожалуйста, сначала изучите её в этой лабораторной работе.

Сначала откройте файл basic_handler.py в файловом менеджере слева и добавьте следующий код. Он определяет простую функцию деления.

def divide(dividend , divisor):
    print(dividend / divisor)

divide(dividend=10, divisor=5)

Теперь запустите скрипт из терминала:

python3 basic_handler.py

Вы увидите правильный результат:

2.0

Теперь давайте посмотрим, что произойдет, если мы попытаемся разделить на ноль. Измените последнюю строку в basic_handler.py, чтобы вызвать divide с делителем 0:

def divide(dividend , divisor):
    print(dividend / divisor)

divide(dividend=10, divisor=0)

Снова запустите скрипт:

python3 basic_handler.py

На этот раз программа аварийно завершится и выведет трассировку (traceback), указывающую на ZeroDivisionError:

Traceback (most recent call last):
  File "/home/labex/project/basic_handler.py", line 4, in <module>
    divide(dividend=10, divisor=0)
  File "/home/labex/project/basic_handler.py", line 2, in divide
    print(dividend / divisor)
ZeroDivisionError: division by zero

Чтобы предотвратить аварийное завершение программы и вместо этого показать понятное пользователю сообщение, мы можем «обработать» исключение с помощью try и except. Код, который может вызвать ошибку, помещается внутрь блока try, а код, который должен выполниться в случае возникновения ошибки, помещается в блок except.

Обновите файл basic_handler.py следующим кодом, чтобы корректно обработать ZeroDivisionError:

## try-except: корректная обработка исключений
def divide(dividend , divisor):
    try:  ## Попытка выполнить этот код
        print(dividend / divisor)
    except ZeroDivisionError:  ## Перехват конкретного типа исключения
        print('You can not divide by 0')

divide(dividend=10, divisor=0)

Теперь запустите скрипт еще раз:

python3 basic_handler.py

Вместо аварийного завершения программа теперь выводит заданное вами полезное сообщение, и выполнение завершается нормально:

You can not divide by 0

Обработка нескольких исключений в одном блоке

На этом этапе вы узнаете, как обрабатывать несколько различных типов исключений в одном блоке except. Это полезно для оптимизации кода обработки ошибок, когда может возникнуть несколько типов проблем.

Вы можете перехватывать кортеж исключений, например (ZeroDivisionError, TypeError), чтобы обрабатывать их с помощью одной и той же логики.

Откройте файл multiple_handler.py и добавьте следующий код. Эта функция спроектирована так, чтобы потенциально вызывать либо TypeError (при попытке сложить строку и целое число), либо ZeroDivisionError.

## Обработка нескольких исключений в одном блоке except
def divide(dividend , divisor):
    try:
        if (dividend == 10):
          var = 'str' + 1  ## Это вызовет TypeError
        else:
          print(dividend / divisor)
    except (ZeroDivisionError, TypeError) as error:  ## Перехват нескольких типов исключений
        print(error)  ## Вывод сообщения об ошибке

divide(dividend=20, divisor=5)

Сначала давайте запустим код в сценарии, где исключение не возникает. Выполните скрипт:

python3 multiple_handler.py

Результатом будет результат деления:

4.0

Теперь давайте вызовем TypeError. Измените последнюю строку multiple_handler.py, чтобы использовать 10 в качестве делимого:

## ... (остальная часть кода) ...
divide(dividend=10, divisor=5)

Снова запустите скрипт:

python3 multiple_handler.py

Блок except перехватывает TypeError и выводит соответствующее сообщение об ошибке:

can only concatenate str (not "int") to str

Наконец, давайте вызовем ZeroDivisionError. Снова измените последнюю строку, на этот раз пытаясь разделить на ноль. Обратите внимание, что условие TypeError (dividend == 10) не выполняется.

## ... (остальная часть кода) ...
divide(dividend=20, divisor=0)

Запустите скрипт:

python3 multiple_handler.py

Тот же блок except теперь перехватывает ZeroDivisionError и выводит свое сообщение:

division by zero

Для успешного прохождения проверки убедитесь, что последний вызов в вашем скрипте — divide(dividend=10, divisor=5), чтобы вызвать TypeError.

Блок finally при обработке исключений

На этом этапе вы поймете назначение и использование блока finally. Код внутри блока finally выполняется всегда, независимо от того, было ли вызвано или обработано исключение. Это делает его идеальным для операций очистки, таких как закрытие файла или сетевого соединения.

Давайте посмотрим, как это работает. Откройте файл finally_handler.py и добавьте следующий код:

## блок finally: выполняется всегда, независимо от исключений
def divide(dividend , divisor):
    try:
        print(dividend / divisor)
    except ZeroDivisionError:
        print('You can not divide by 0')
    finally:  ## Выполняется всегда, даже если возникло исключение
        print('Execution finished')

divide(dividend=10, divisor=5)

Запустите скрипт из терминала:

python3 finally_handler.py

В этом случае исключение не возникает. Блок try завершается успешно, а затем выполняется блок finally.

2.0
Execution finished

Теперь давайте добавим ошибку. Измените последнюю строку в finally_handler.py, чтобы делить на ноль:

## ... (остальная часть кода) ...
divide(dividend=10, divisor=0)

Снова запустите скрипт:

python3 finally_handler.py

На этот раз возникает ZeroDivisionError, который перехватывается блоком except. Несмотря на то, что исключение произошло, блок finally все равно выполняется после завершения блока except.

You can not divide by 0
Execution finished

Как видите, сообщение "Execution finished" выводится в обоих сценариях, что доказывает, что блок finally является надежным местом для кода, который должен выполняться всегда.

Пользовательские исключения

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

Пользовательские исключения определяются путем создания нового класса, который наследуется от базового класса Exception в Python. Затем вы можете вызвать это исключение с помощью ключевого слова raise.

Откройте файл custom_exception.py и добавьте следующий код для определения и вызова простого пользовательского исключения:

## Пользовательское исключение: создается путем наследования от класса Exception
class MyCustomException(Exception):
    pass

raise MyCustomException  ## Вызов пользовательского исключения

Если вы запустите этот скрипт, он немедленно остановится и покажет трассировку для вашего нового исключения:

python3 custom_exception.py
Traceback (most recent call last):
  File "/home/labex/project/custom_exception.py", line 5, in <module>
    raise MyCustomException  ## Вызов пользовательского исключения
__main__.MyCustomException

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

class MyCustomException(Exception):
    pass

raise MyCustomException('A custom message for my custom exception')

Запуск этого кода покажет ваше сообщение в трассировке:

Traceback (most recent call last):
  File "/home/labex/project/custom_exception.py", line 4, in <module>
    raise MyCustomException('A custom message for my custom exception')
__main__.MyCustomException: A custom message for my custom exception

Обработка пользовательского исключения выполняется так же, как и встроенного: с помощью блока try...except. Обновите custom_exception.py до его финальной формы, которая вызывает и обрабатывает пользовательское исключение:

class MyCustomException(Exception):
    pass

try:
    raise MyCustomException('A custom message for my custom exception')
except MyCustomException:
    print('My custom exception was raised')

Теперь запустите скрипт в последний раз:

python3 custom_exception.py

Программа больше не аварийно завершается. Вместо этого она выполняет код в блоке except и выводит ваше подтверждающее сообщение:

My custom exception was raised

Резюме

В этой лабораторной работе вы изучили основные методы обработки исключений в Python. Вы начали с базовых блоков try и except для перехвата конкретных ошибок и предотвращения аварийного завершения программы. Затем вы узнали, как эффективно обрабатывать несколько типов исключений в одном блоке except. Вы также изучили блок finally, который критически важен для кода очистки, который должен выполняться всегда. Наконец, вы научились создавать, вызывать и обрабатывать собственные пользовательские исключения, что позволит вам создавать более надежную и описательную логику обработки ошибок в ваших приложениях.