Обработка исключений с помощью try except в Python

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

Введение

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

Обработка ValueError с помощью try except

На этом шаге вы научитесь обрабатывать ValueError с помощью оператора try...except. ValueError возникает, когда функция получает аргумент правильного типа, но недопустимого значения. Распространенным примером является попытка преобразовать строку, не являющуюся числом, в целое число с помощью функции int().

Мы напишем скрипт на Python, который запрашивает у пользователя целое число. Если введенное значение не является допустимым целым числом, мы перехватим ValueError и выведем дружественное сообщение для пользователя.

В проводнике файлов WebIDE слева найдите и откройте файл handle_value_error.py, расположенный в каталоге ~/project. Затем добавьте в него следующий код:

while True:
    try:
        x = int(input('Please enter an integer: '))
        print(f'You entered: {x}')
        break
    except ValueError:
        print('That was not a valid integer. Please try again.')

Сохраните файл.

Теперь откройте интегрированный терминал и запустите скрипт с помощью команды python:

python ~/project/handle_value_error.py

Скрипт предложит вам ввести целое число. Сначала попробуйте ввести нецелое значение, например hello, чтобы увидеть обработку ошибок в действии. Затем введите допустимое целое число, например 123, чтобы увидеть успешный путь выполнения.

Пример вывода:

Please enter an integer: hello
That was not a valid integer. Please try again.
Please enter an integer: 123
You entered: 123

В этом коде:

  • Цикл while True: гарантирует, что программа продолжает запрашивать ввод до тех пор, пока не будет введено допустимое целое число.
  • Блок try содержит код, который может вызвать исключение, в частности вызов int(input(...)).
  • Если в блоке try возникает ValueError, выполняется код внутри блока except ValueError:.
  • Если исключение не возникает, выполняется оператор print(), и break выходит из цикла.

Это демонстрирует, как try...except ValueError позволяет корректно обрабатывать недопустимый ввод без аварийного завершения программы.

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

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

Давайте напишем скрипт, который выполняет деление. Эта операция может вызвать ValueError, если ввод не является числом, и ZeroDivisionError, если делитель равен нулю.

В WebIDE откройте файл ~/project/handle_multiple_exceptions.py и добавьте следующий код. Эта версия использует отдельные блоки except для каждого типа ошибки.

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except ValueError:
    print("Invalid input. Please enter integers only.")
except ZeroDivisionError:
    print("Error: Division by zero is not allowed.")

Сохраните файл и запустите его из терминала:

python ~/project/handle_multiple_exceptions.py

Протестируйте скрипт с различными входными данными, чтобы вызвать каждое исключение:

  1. Введите нецелое значение (например, abc).
  2. Введите 0 в качестве делителя.
  3. Введите допустимые целые числа для обоих операндов.

Пример вывода при делении на ноль:

Enter the numerator: 10
Enter the denominator: 0
Error: Division by zero is not allowed.

Вы также можете обрабатывать несколько исключений с помощью одного оператора except, группируя их в кортеж (tuple). Это полезно, когда для разных ошибок требуется выполнить одно и то же действие.

Теперь обновите код в ~/project/handle_multiple_exceptions.py, чтобы использовать этот сгруппированный подход:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")

Сохраните файл и запустите его снова с теми же тестовыми случаями, чтобы увидеть новое, объединенное сообщение об ошибке.

Выполнение кода с блоком else

Оператор try может включать необязательный блок else. Код в блоке else выполняется только в том случае, если блок try завершился без возникновения каких-либо исключений. Это полезно для отделения кода, который должен выполняться при успехе, от основного блока try.

Давайте модифицируем наш скрипт деления, чтобы включить блок else.

В WebIDE откройте файл ~/project/try_except_else.py и добавьте следующий код:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")
else:
    print("Division successful!")
    print(f"Result: {result}")

Сохраните файл и запустите его из терминала:

python ~/project/try_except_else.py

Протестируйте скрипт как с неудачными, так и с успешными входными данными:

  1. Введите 0 в качестве делителя, чтобы вызвать исключение.
  2. Введите допустимые, ненулевые целые числа, чтобы выполнить блок else.

Примеры вывода:

Enter the numerator: 10
Enter the denominator: 0
An error occurred: Invalid input or division by zero.
Enter the numerator: 10
Enter the denominator: 2
Division successful!
Result: 5.0

Как видите, сообщения в блоке else выводятся только при успешном выполнении деления. Если возникает исключение, выполняется блок except, а блок else пропускается.

Обеспечение выполнения кода с блоком finally

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

Давайте добавим блок finally к нашему примеру с делением.

В WebIDE откройте файл ~/project/try_except_finally.py и добавьте следующий код:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")
finally:
    print("Execution finished.")

Сохраните файл и запустите его из терминала:

python ~/project/try_except_finally.py

Запустите скрипт как с неудачными, так и с успешными входными данными:

  1. Введите 0 в качестве делителя, чтобы вызвать исключение.
  2. Введите допустимые, ненулевые целые числа.

Примеры вывода:

Enter the numerator: 10
Enter the denominator: 0
An error occurred: Invalid input or division by zero.
Execution finished.
Enter the numerator: 10
Enter the denominator: 2
Result: 5.0
Execution finished.

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

Генерация пользовательских исключений

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

Сначала посмотрим, как вызвать (raise) встроенное исключение. В WebIDE откройте файл ~/project/raise_exception.py и добавьте следующий код:

def check_positive(number):
    if number <= 0:
        raise ValueError("Input must be a positive number")
    print(f"The number {number} is positive.")

try:
    check_positive(-5)
except ValueError as e:
    print(f"Caught an exception: {e}")

try:
    check_positive(10)
except ValueError as e:
    print(f"Caught an exception: {e}")

Сохраните файл и запустите его из терминала:

python ~/project/raise_exception.py

Вывод будет следующим:

Caught an exception: Input must be a positive number
The number 10 is positive.

Здесь функция check_positive вызывает исключение ValueError, если входное значение не является положительным, которое затем перехватывается блоком except.

Теперь определим и вызовем пользовательское исключение. Пользовательские исключения — это классы, которые наследуются от встроенного класса Exception.

В WebIDE откройте файл ~/project/custom_exception.py и добавьте следующий код:

class NegativeNumberError(Exception):
    """Custom exception raised for negative numbers."""
    pass

def process_positive_number(number):
    if number < 0:
        raise NegativeNumberError("Negative numbers are not allowed")
    print(f"Processing positive number: {number}")

try:
    process_positive_number(-10)
except NegativeNumberError as e:
    print(f"Caught custom exception: {e}")

try:
    process_positive_number(20)
except NegativeNumberError as e:
    print(f"Caught custom exception: {e}")

Сохраните файл и запустите его из терминала:

python ~/project/custom_exception.py

Вывод будет следующим:

Caught custom exception: Negative numbers are not allowed
Processing positive number: 20

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

Резюме

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