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

Зачем нужна обработка исключений?

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

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

Базовая конструкция try-except

Для обработки исключений в Python используется конструкция try-except. В блоке try размещается код, который может вызвать ошибку. Если ошибка возникает, управление передаётся в блок except.

Пример:

try:
    x = int(input("Введите число: "))
    y = int(input("Введите ещё одно число: "))
    result = x / y
    print(f"Результат: {result}")
except ZeroDivisionError:
    print("Ошибка: деление на ноль!")
except ValueError:
    print("Ошибка: неверный ввод, нужно ввести число.")

Здесь обрабатываются два типа исключений:

  • ZeroDivisionError — возникает при делении на ноль.
  • ValueError — возникает, если введённые данные не могут быть преобразованы в число.

Блок else

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

Пример:

try:
    x = int(input("Введите число: "))
    y = int(input("Введите ещё одно число: "))
    result = x / y
except ZeroDivisionError:
    print("Ошибка: деление на ноль!")
except ValueError:
    print("Ошибка: неверный ввод.")
else:
    print(f"Результат: {result}")

В этом случае блок else выполнится только если не возникнет ни одной ошибки.

Блок finally

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

Пример:

try:
    x = int(input("Введите число: "))
    y = int(input("Введите ещё одно число: "))
    result = x / y
    print(f"Результат: {result}")
except ZeroDivisionError:
    print("Ошибка: деление на ноль!")
except ValueError:
    print("Ошибка: неверный ввод.")
finally:
    print("Программа завершена.")

Блок finally всегда выполняется, даже если в процессе выполнения произошла ошибка.

Создание собственных исключений

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

Пример создания собственного исключения:

class NegativeNumberError(Exception):
    pass

def check_positive(number):
    if number < 0:
        raise NegativeNumberError("Число не должно быть отрицательным!")

try:
    x = int(input("Введите положительное число: "))
    check_positive(x)
except NegativeNumberError as e:
    print(e)
except ValueError:
    print("Ошибка: неверный ввод.")

В этом примере мы создаём своё исключение NegativeNumberError, которое вызывается, если число отрицательное.

Пример программы: Калькулятор с обработкой ошибок

Теперь создадим калькулятор, который обрабатывает следующие ошибки:

  • Деление на ноль.
  • Неверный ввод данных (например, если введены буквы вместо чисел).
class CalculatorError(Exception):
    """Базовый класс для других исключений"""
    pass

class DivisionByZeroError(CalculatorError):
    """Исключение для деления на ноль"""
    pass

class InvalidInputError(CalculatorError):
    """Исключение для неверного ввода данных"""
    pass

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    if y == 0:
        raise DivisionByZeroError("Нельзя делить на ноль!")
    return x / y

def get_number(prompt):
    try:
        return float(input(prompt))
    except ValueError:
        raise InvalidInputError("Ошибка: введено не число!")

def main():
    try:
        num1 = get_number("Введите первое число: ")
        num2 = get_number("Введите второе число: ")
        operation = input("Введите операцию (+, -, *, /): ")

        if operation == '+':
            result = add(num1, num2)
        elif operation == '-':
            result = subtract(num1, num2)
        elif operation == '*':
            result = multiply(num1, num2)
        elif operation == '/':
            result = divide(num1, num2)
        else:
            raise InvalidInputError("Ошибка: неверная операция.")

        print(f"Результат: {result}")
    except CalculatorError as e:
        print(e)
    finally:
        print("Калькулятор завершил работу.")

if __name__ == "__main__":
    main()

Разбор программы:

  • Определение пользовательских исключений: Мы создали два пользовательских исключения: DivisionByZeroError для деления на ноль и InvalidInputError для неправильного ввода.
  • Функции для арифметических операций: Каждая арифметическая операция (сложение, вычитание, умножение и деление) оформлена как отдельная функция.
  • Функция get_number(): Эта функция запрашивает ввод числа у пользователя и обрабатывает ошибку, если ввод не является числом, выбрасывая исключение InvalidInputError.
  • Основная логика программы: Программа запрашивает два числа и операцию у пользователя. Если операция правильная, выполняется соответствующее арифметическое действие. Если ввод или операция некорректны, программа выводит сообщение об ошибке.
  • Обработка ошибок: Все ошибки обрабатываются с использованием наших собственных исключений, что делает программу более гибкой и понятной.

Заключение

Обработка ошибок и исключений — важный аспект написания надёжных программ. Используя блоки try, except, finally, мы можем перехватывать ошибки и продолжать выполнение программы, избегая её неожиданного завершения. Кроме того, создание собственных исключений помогает лучше управлять логикой программы и предоставлять более точные сообщения об ошибках.