Исключения
В современных программах передача управления происходит не всегда так гладко, как в описанных выше конструкциях. Для обработки особых ситуаций (таких как деление на ноль или попытка чтения из несуществующего файла) применяется механизм исключений. Лучше всего пояснить синтаксис оператора try-except следующим примером:
try: res = int(open('a.txt').read()) / int(open('c.txt').read()) print res except IOError: print "Ошибка ввода-вывода" except ZeroDivisionError: print "Деление на 0" except KeyboardInterrupt: print "Прерывание с клавиатуры" except: print "Ошибка"
В этом примере берутся числа из двух файлов и делятся одно на другое. В результате этих нехитрых действий может возникнуть несколько исключительных ситуаций, некоторые из них отмечены в частях except (здесь использованы стандартные встроенные исключения Python). Последняя часть except в этом примере улавливает все другие исключения, которые не были пойманы выше. Например, если хотя бы в одном из файлов находится нечисловое значение, функция int() возбудит исключение ValueError. Его-то и сможет отловить последняя часть except. Разумеется, выполнение части try в случае возникновения ошибки уже не продолжается после выполнения одной из частей except.
В отличие от других языков программирования, в Python исключения нередко служат для упрощения алгоритмов. Записывая оператор try-except, программист может думать так: "попробую, а если сорвется - выполнится код в except". Особенно часто это используется для выражений, в которых значение получается по ключу из отображения:
try: value = dict[key] except: value = default_value
Вместо
if dict.has_key(key): value = dict[key] else: value = default_value
Примечание: Пример уже несколько устаревшей идиомы языка Python иллюстрирует только дух этого подхода: в современном Python лучше записать так value = dict.get(key, default_value). |
Исключения можно возбуждать и из программы. Для этого служит оператор raise. Заодно следующий пример показывает канонический способ определения собственного исключения:
class MyError(Exception): pass
try: ... raise MyError, "my error 1" ... except MyError, x: print "Ошибка:", x
Кстати, все исключения выстроены в иерархию классов, поэтому ZeroDivisionError может быть поймана как ArithmeticError, если соответствующая часть except будет идти раньше.
Для утверждений применяется специальный оператор assert. Он возбуждает AssertionError, если заданное в нем условие неверно. Этот оператор используют для самопроверки программы. В оптимизированном коде он не выполняется, поэтому строить на нем логику алгоритма нельзя. Пример:
c = a + b assert c == a + b
Кроме описанной формы оператора, есть еще форма try-finally для гарантированного выполнения некоторых действий при передаче управления изнутри оператора try-finally вовне. Он может применяться для освобождения занятых ресурсов, что требует обязательного выполнения, независимо от произошедших внутри катаклизмов:
try: ... finally: print "Обработка гарантированно завершена"
Смешивать вместе формы try-except и try-finally нельзя.