Сообщить об ошибке.

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

Можно писать программы, которые обрабатывают выбранные исключения. Посмотрите на следующий пример, который запрашивает ввод у пользователя до тех пор, пока не будет введено правильное целое число, но позволяет пользователю прерывать программу, используя Ctrl-C или что-либо поддерживаемое операционной системой. Обратите внимание, что сгенерированное пользователем прерывание сигнализируется возбуждением исключения KeyboardInterrupt.

while True:
     try:
         x = int(input("Пожалуйста, введите целое число: "))
         break
     except ValueError:
         print("Это не целое число. Попробуйте снова...")

Оператор try/except работает следующим образом:

  • Сначала выполняется инструкция try - код между ключевыми словами try и except.
  • Если исключение не возникает, инструкция except пропускается и выполнение оператора try завершается.
  • Если во время выполнения кода в инструкции try возникает исключение, остальная часть кода этого блока пропускается. Затем, если тип исключения соответствует исключению, записанному после ключевого слова except, блок кода этой инструкции except выполняется, а затем выполнение программы продолжается после всей конструкции try.
  • Если возникает исключение, которое не соответствует исключению, записанному в инструкции except, оно передается внешним операторам try. Если обработчик не найден, то это считается необработанным исключением, следовательно выполнение останавливается с сообщением об ошибке выполнения.

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

except (RuntimeError, TypeError, NameError):
    pass

Класс в инструкции except совместим с исключением, если это тот же самый класс или его базовый класс, но не наоборот. Инструкция except, перечисляющая производный класс, не совместима с базовым классом. Например, следующий код будет печатать B, C, D в таком порядке:

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print('D')
    except C:
        print('C')
    except B:
        print('B')

Обратите внимание, что если бы инструкции except были отменены (с первым исключением B), то вывелось бы B, B, B - срабатывает первое совпадающее предложение except.

Последняя инструкция except может опустить имя исключения. Используйте это с крайней осторожностью, таким образом можно легко замаскировать реальную ошибку программирования! Здесь можно использовать sys.exc_info() или модуль traceback для вывода сообщения об ошибке или ее сохранения для дельнейшего анализа, а затем повторно вызвать исключение при помощи оператора raise, что позволяет вызывающей стороне также обработать исключение:

import traceback

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Не удалось преобразовать данные в целое число.")
except:
    print("Непредвиденная ошибка...")
    # сохраняем исключение для дальнейшего анализа.
    with open('trace.txt', 'a') as fp:
        traceback.print_exc(file=fp)
    # повторный вызов исключения, если это необходимо.
    raise

В примере выше, сведения об ошибке сохраняются в файл trace.txt с использованием встроенной функции open(). Лучшей практикой сохранения исключений для дальнейшего анализа в подобных ситуациях является применение модуля logging.

Конструкция try/except может содержать необязательную инструкцию else, которая при наличии должна следовать за всеми инструкциями except. Блок кода в инструкции else будет выполнен в том случае, если код в инструкции try не вызывает исключения. Например:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('не удается открыть', arg)
    else:
        print(arg, 'имеет', len(f.readlines()), 'строк')
        f.close()

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

Когда возникает исключение, оно может иметь связанное значение, также известное как аргумент исключения. Наличие и тип аргумента зависят от типа исключения.

Инструкция except может указывать переменную после имени исключения. Переменная привязывается к экземпляру исключения с аргументами, хранящимися в экземпляре instance.args. Для удобства, экземпляр исключения определяет __str__(), так что аргументы могут быть напечатаны непосредственно без необходимости ссылаться instance.args. Кроме того, можно создать экземпляр исключения прежде, чем вызвать его и добавить любые атрибуты к нему по желанию.

try:
    raise Exception('spam', 'eggs')
except Exception as inst:
    # экземпляр исключения
    print(type(inst))
    # аргументы, хранящиеся внутри
    print(inst.args)
    # __str__ позволяет печатать args напрямую, но может
    # быть переопределен в подклассах исключений
    print(inst)
    # распаковка аргументов
    x, y = inst.args
    print('x =', x)
    print('y =', y)

# <class 'Exception'>
# ('spam', 'eggs')
# ('spam', 'eggs')
# x = spam
# y = eggs

Если исключение имеет аргументы, они печатаются как последняя часть ("деталь") сообщения для необработанных исключений.

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

def this_fails():
    x = 1/0

try:
    this_fails()
except ZeroDivisionError as err:
    print('Handling run-time error:', err)

#Handling run-time error: division by zero