Можно писать программы, которые обрабатывают выбранные исключения. Посмотрите на следующий пример, который запрашивает ввод у пользователя до тех пор, пока не будет введено правильное целое число, но позволяет пользователю прерывать программу, используя 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