Ошибки и исключения
До сих пор мы лишь упоминали об ошибках, но если Вы пробовали приведенные примеры, то могли увидеть некоторые из них. Можно выделить (как минимум) два различимых типа ошибок: синтаксические ошибки и исключения [Синтаксические ошибки также являются исключе
🕛 11.07.2009, 14:24
Синтаксические ошибкиСинтаксические ошибки, пожалуй, чаще всего встречаются во время изучения языка:
>>> while 1 print 'Hello world' File "<stdin>", line 1 while 1 print 'Hello world'^
SyntaxError: invalid syntax
Синтаксический анализатор выводит строку, содержащую ошибку, и указывает место, где ошибка была обнаружена. Ошибка обычно вызвана лексемой, предшествующей стрелке: в приведенном примере, ошибка обнаружена на месте ключевого слова print, так как перед ним отсутствует двоеточие (':'). Имя файла и номер строки выводится для того, чтобы Вы знали, где искать ошибку в случае, если инструкции считываются из файла.
Исключения
Даже если инструкция или выражение синтаксически верно, ошибка может произойти при попытке выполнения. Ошибки, обнаруженные во время выполнения, не являются безусловно фатальными, и скоро Вы узнаете, как их можно обрабатывать в программах на языке Python. Большинство исключений, однако, не обрабатываются программами и приводят к сообщениям об ошибке:
>>> 10 * (1/0)
Traceback (innermost last): File "<stdin>", line 1
ZeroDivisionError: integer division or modulo
>>> 4 + spam*3
Traceback (innermost last): File "<stdin>", line 1
NameError: spam
>>> '2' + 2
Traceback (innermost last): File "<stdin>", line 1
TypeError: illegal argument type for built-in operation
Последняя строка сообщения показывает, что произошло. Исключения бывают разного типа - он выводится в сообщении. Типы исключений в приведенном примере: ZeroDivisionError, NameError и TypeError. Имена стандартных исключений являются встроенными идентификаторами, но не являются зарезервированными ключевыми словами.
Сразу после типа выводятся подробности возникновения исключения. Предшествующая часть сообщения об ошибке показывает контекст возникновения исключительной ситуации в форме содержимого стека. При этом выводятся строки исходного текста, за исключением строк, читаемых со стандартного ввода.
В главе 13 перечислены все встроенные исключения и их назначение.
Обработка исключений
Вы можете писать программы, которые будут обрабатывать определенные исключения. Посмотрите на следующий пример, в котором пользователю будет выдаваться приглашение до тех пор, пока не будет введено целое число или выполнение не будет прервано (обычно Ctrl-C). Во втором случае генерируется исключение KeyboardInterrupt.
>>> while 1:
... try:
... x = int(raw_input(
... "Введите, пожалуйста, целое число: "))
... break
... except ValueError:
... print "Вы ошиблись. Попробуйте еще раз..."
...
Инструкция try работает следующим образом.
* Сначала выполняется ветвь try (инструкции, находящиеся между ключевыми словами try и except). * Если не возникает исключительной ситуации, ветвь except пропускается и выполнение инструкции try завершается. * Если во время выполнения ветви try генерируется исключение, оставшаяся часть ветви пропускается. Далее, если тип (класс) исключения соответствует указанному после ключевого слова except, выполняется ветвь except и выполнение инструкции try завершается. * Если исключение не соответствует указанному после ключевого слова except, то оно передается внешнему блоку try или, если обработчик не найден, исключение считается не перехваченным, выполнение прерывается и выводится сообщение об ошибке.
Инструкция try может иметь более одной ветви except, определяя обработчики для разных исключений. Выполняться будет (как максимум) только один из них. Обрабатываются только исключения, сгенерированные в соответствующей ветви try, но не в других обработчиках инструкции try. После ключевого слова except может быть указано несколько типов исключений в виде кортежа:
... except (RuntimeError, TypeError, NameError):
... pass
В последней ветви except тип исключения может быть опущен - в этом случае будут обрабатываться все исключения. Используйте такую запись с особой осторожностью - так Вы легко можете замаскировать реальные ошибки! Перехват всех исключений можно использовать для вывода сообщения об ошибке, а затем повторно сгенерировать его (позволяя обрабатывать исключение в другом месте):
import sys
try: f = open('myfile.txt') s = f.readline() i = int(s.strip())
except IOError, exc: print "Ошибка ввода/вывода", exc
except ValueError: print "Не могу преобразовать данные к целому типу."
except: print "Неожиданная ошибка:", sys.exc_info()[0] raise # повторно генерирует последнее # перехваченное исключение
После всех ветвей except, инструкция try может содержать ветвь else, которая будет выполняться в случае, если во время выполнения ветви try исключения не генерируются. Например:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'Не могу открыть', arg else: print arg, 'содержит', len(f.readlines()), \ 'строк' f.close()
Обычно лучше использовать ветвь else, чем добавлять код в основную ветвь инструкции try, так как это позволяет избежать обработки исключений, сгенерированных кодом, который Вы вовсе не собирались защищать.
Исключение может иметь ассоциированное с ним значение - аргумент, переданный классу исключения при инициализации. Наличие и тип аргумента зависит от типа исключения. Ассоциированное значение используется при получении для исключения строкового значения. Чтобы получить значение исключения, в ветви except после класса исключения (или кортежа классов) укажите переменную:
>>> try:
... spam()
... except NameError, x:
... print 'eIN', x, 'не определено'
...
Имя spam не определено
Если исключение не обрабатывается, значение исключения выводится в сообщении об ошибке после имени класса исключения.
Обработчик перехватывает не только исключения, сгенерированные непосредственно в блоке try, но и в вызываемых из него функциях. Например:
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError, exc:
... print 'Ошибка времени выполнения:', exc
...
Ошибка времени выполнения: integer division or modulo
Генерация исключений
Инструкция raise позволяет программисту принудительно сгенерировать исключение. Например:
>>> raise NameError('HiThere')
Traceback (innermost last): File "<stdin>", line 1
NameError: HiThere
В качестве аргумента raise используется экземпляр класса. Класс указывает на тип исключения; аргумент, передаваемый конструктору, обычно описывает "подробности" возникновения исключительной ситуации.
Исключения, определяемые пользователем
Вы можете использовать свои собственные исключения, используя строковые выражения для обозначения его имени (устаревший способ) или создавая новые классы исключения. Например:
>>> class MyError(Exception): pass
...
>>> try:
... raise MyError(2*2)
... except MyError, e:
... print 'Исключение MyError, value равно', e
...
Исключение MyError, value равно 4
>>> raise MyError(1)
Traceback (innermost last): File "<stdin>", line 1
_main_.MyError: 1
Подробную информацию о классах Вы можете получить в главе 9.
"Страхование" от ошибок
Еще один вариант записи инструкции try - с определением "страховочной" ветви finally, которая будет выполняться при любых обстоятельствах. Например:
>>> try:
... raise KeyboardInterrupt()
... finally:
... print 'До свидания!'
...
До свидания!
Traceback (innermost last): File "<stdin>", line 2
KeyboardInterrupt
Ветвь finally выполняется независимо от того, возникла ли исключительная ситуация во время выполнения блока try или нет, в том числе и если выход происходит по инструкции break или return.
Инструкция try может иметь либо одну или несколько ветвей except, либо одну ветвь finally, но не оба варианта сразу.