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