Обработка исключений в Python
При возникновении ошибки времени выполнения Python создает (возбуждает) специальный объект - исключение, который позволяет однозначно характеризовать возникшую ошибочную ситуацию. Выбор подходящего исключения происходит из встроенной иерархии классов-исключений (фрагмент):
BaseException(базовое исключение)SystemExit(исключение, порождаемое функциейsys.exit()при выходе из программы)KeyboardInterrupt(прерывании программы пользователем,Ctrl+C)Exception(базовое несистемное исключение)ArithmeticError(арифметическая ошибка)FloatingPointError(неудачное выполнение операции с плавающей запятой)OverflowError(результат арифметической операции слишком велик для представления)ZeroDivisionError(деление на ноль)
LookupError(некорректный индекс или ключ)IndexError(индекс не входит в диапазон элементов)KeyError(несуществующий ключ)
MemoryError(недостаточно памяти)NameError(не найдено переменной с таким именем)OSError(ошибка, связанная с ОС - есть подклассы, напримерFileNotFoundError)SyntaxError(синтаксическая ошибка, включает классыIndentationErrorиTabError)SystemError(внутренняя ошибка)TypeError(операция применена к объекту несоответствующего типа)ValueError(аргумент правильного типа, но некорректного значения)
Пример встроенного возбуждения исключения:
>>> "я - строка" / 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'str' and 'int'Конструкция try
tryПридерживаясь идеологии «Легче попросить прощения, чем разрешения», Python предусматривает конструкцию try для обработки возникающих исключений.
tryexceptelsefinally
try:                              # (try строго 1)
    try_ suite                    # код, который может выполниться с ошибкой
except exception_group1 as var1:  # (except - 0 (если есть finally) и более)
    except_suite1                 # код, выполняемый в случае исключения 'exception_group1'
...                               # ссылка на исключение может быть записана в 'var1'
except exception_groupN as varN:
    except_suiteN                 # код, выполняемый в случае исключения 'exception_groupN'
...                               # except-блоков может быть произвольное кол-во
else:                             # (else - 0 или 1)
    else_suite                    # выполняется, если try не завершен преждевременно (например, break)
finally:                          # (finally - 0 или 1)
    finally_suite                 # код, который должен выполнится всегда (была ошибка выше или нет)Ход выполнения:
код, который потенциально может привести к ошибке, помещается в блок
try;в случае ошибки, код немедленно завершается и переходит в обработчик
except(если он указан для соответствующего исключения);
Наиболее простой способ обработки исключений
try:
    x = int(input("Введите целое число x (для вычисления 1/x): "))
    res = 1 / x
    print("1/{} = {:.2f}".format(x, res))
except:
    print("Произошла ошибка!")
# --------------
# Примеры вывода:
# Введите целое число x (для вычисления 1/x): 3
# 1/3 = 0.33
# Введите целое число x (для вычисления 1/x): qwerty
# Произошла ошибка!Подобный вариант обработки исключений не рекомендуется, т.к. блок except будет перехватывать любое исключение, что не позволит точно определить ошибку в коде. Улучшить код можно, добавив обработку исключения по классу (Листинг 7.1.4).
Обработка общего класса исключений Exception
try:
    x = int(input("Введите целое число x (для вычисления 1/x): "))
    res = 1 / x
    print("1/{} = {:.2f}".format(x, res))
except Exception as err:
    print("Произошла ошибка!")
    print("Тип:", type(err))
    print("Описание:", err)
# --------------
# Примеры вывода:
# Введите целое число x (для вычисления 1/x): 3
# 1/3 = 0.33
# Введите целое число x (для вычисления 1/x): 5.5
# Произошла ошибка!
# Тип: <class 'ValueError'>
# Описание: invalid literal for int() with base 10: '5.5'Рекомендуемым способом обработки исключений является как можно большая конкретизация класса исключения.
Обработка конкретных классов исключений
try:
    x = int(input("Введите целое число x (для вычисления 1/x): "))
    res = 1 / x
    print("1/{} = {:.2f}".format(x, res))
except ZeroDivisionError:
    print("На ноль делить нельзя!")
except ValueError as err:  # 'err' содержит ссылку на исключение
    print("Будьте внимательны:", err)
except (FileExistsError, FileNotFoundError):  # Исключения можно перечислять в виде кортежа
    print("Этого никогда не случится - мы не работаем с файлами")
except Exception as err:
    # Все, что не обработано выше и является потомком 'Exception',
    # будет обработано здесь
    print("Произошла ошибка!")
    print("Тип:", type(err))
    print("Описание:", err)
# --------------
# Примеры вывода:
# Введите целое число x (для вычисления 1/x): 3
# 1/3 = 0.33
# Введите целое число x (для вычисления 1/x): 0
# На ноль делить нельзя!
# Введите целое число x (для вычисления 1/x): qwerty
# Будьте внимательны: invalid literal for int() with base 10: 'qwerty'Возбуждение исключений (raise)
raise)Исключения являются не только механизмом обработки ошибок, но и удобным средством управления потоком выполнения. Так, необходимое исключение можно возбудить вручную, когда это необходимо, используя конструкцию raise.
raise
raise exception(args)  # явное указание класса возбуждаемого исключения
# или
raise                  # 1) повторное возбуждение активного исключения (re-raise)
                       #    внутри блока except
                       # 2) 'TypeError' по умолчаниюВозбуждаемое исключение может быть как встроенным (если соответствует по смыслу), так и пользовательским (создаваемым самостоятельно).
В Листинге 7.1.6 приведен пример использование оператора raise.
Листинг 7.1.6 - Использование raise для управления потоком выполнения | скачать
MIN = 1
MAX = 10
try:
    x = int(input("Введите целое число от {} до {}: ".format(MIN, MAX)))
    if not MIN <= x <= MAX:
        # Возбудив исключение, его можно будет обработать в except
        # вместе с другими похожими исключениями
        raise ValueError("Число лежит вне интервала [{}; {}]!".format(MIN, MAX))
    print("Спасибо!")
except ValueError as err:  # 'err' содержит ссылку на исключение
    print("Будьте внимательны:", err)
# --------------
# Примеры вывода:
# Введите целое число от 1 до 10: 5
# Спасибо!
# Введите целое число от 1 до 10: 15
# Будьте внимательны: Число лежит вне интервала [1; 10]!
# Введите целое число от 1 до 10: qwerty
# Будьте внимательны: invalid literal for int() with base 10: 'qwerty'Особенности обработки исключений внутри функций
Обработка исключений аналогично производится и внутри функций, однако, необходимо писать код так, чтобы вызывающий код знал о случившемся, если это влияет на его дальнейшую работу.
Разница в обработке исключений приведена в Листинге 7.1.7.
Листинг 7.1.7 - Различная обработка исключений в функции | скачать
# Ниже представлены 3 варианта обработки исключений в функциях
# Основное правило - обработка исключений внутри возможна и нужна, однако
#                    вызывающий код должен также знать о случившемся, если
#                    влияет на дальнейшую работу
def get_1_x(x):
    """Вернуть 1/x.
    Функция не обрабатывает исключения - ответственность на вызывающем коде.
    """
    return 1/x
def get_2_x(x):
    """Вернуть 2/x.
    Функция обрабатывает исключения, "затушив" ошибку - вызывающий код
    не будет знать, сработала функция правильно или нет.
    Данный способ использовать не рекомендуется!
    """
    try:
        return 2/x
    except Exception as e:
        print("Внутри произошла ошибка...", e)
def get_3_x(x):
    """Вернуть 3/x.
    Функция не только обрабатывает исключения, но перевозбуждает его:
    в результате вызывающий так же получает возникшее исключение.
    Внутренняя обработка исключений может быть полезна, если в целом результат
    функции не связан с внутренней ошибкой.
    """
    try:
        return 3/x
    except Exception as e:
        print("Внутри произошла ошибка...", e)
        raise
funcs = (get_1_x, get_2_x, get_3_x)
# Вызываем каждую функцию с "ошибочным" параметром
for func in funcs:
    try:
        print("-" * 50)
        print("Запущена функция:", func.__name__)
        print(func(0))
    except Exception as e:
        print("Произошла ошибка: {}.".format(e))
# -------------
# Пример вывода:
# --------------------------------------------------
# Запущена функция: get_1_x
# Произошла ошибка: division by zero.
# --------------------------------------------------
# Запущена функция: get_2_x
# Внутри произошла ошибка... division by zero
# None
# --------------------------------------------------
# Запущена функция: get_3_x
# Внутри произошла ошибка... division by zero
# Произошла ошибка: division by zero.
Last updated