Математический анализ. 3е издание
Скачать 4.86 Mb.
|
try: ... fetcher(x, 4) ... except IndexError: ... print 'got exception' got exception >>> Теперь, когда исключение будет возникать при выполнения инструк ций в блоке try, интерпретатор будет автоматически переходить к ва шему обработчику (блок под предложением except, в котором указано имя исключения). При работе в интерактивной оболочке, как в приме ре выше, после выполнения блока except происходит возврат в пригла шение к вводу. В настоящих программах инструкции try не только пе рехватывают исключения, но и выполняют действия по восстановле+ нию после ошибок: >>> def catcher(): ... try: ... fetcher(x, 4) ... except IndexError: ... print 'got exception' ... print 'continuing' >>> catcher() got exception continuing >>> На этот раз после того, как исключение было перехвачено и обработа но, программа продолжила выполнение ниже всей инструкции try – 706 Глава 27. Основы исключений именно поэтому в данном примере было выведено сообщение «continu ing». Стандартное сообщение об ошибке не появилось на экране, и про грамма продолжила работу как ни в чем не бывало. Исключения могут возбуждаться интерпретатором или самой про граммой, и могут перехватываться или не перехватываться. Чтобы возбудить исключение вручную, достаточно просто выполнить инст рукцию raise (или assert, с условным выражением, возвращающим ложь). Исключения, определяемые программой, перехватываются точно так же, как и встроенные исключения: >>> bad = 'bad' >>> try: ... raise bad ... except bad: ... print 'got bad' got bad Если исключение, определяемое программой, не перехватывается, оно будет передано обработчику исключений по умолчанию, что приведет к завершению программы с выводом стандартного сообщения об ошиб ке. В данном случае стандартное сообщение включает текст строки, использовавшейся для идентификации исключения: >>> raise bad Traceback (most recent call last): File " ", line 1, in ? raise bad bad В других случаях сообщение об ошибке может включать текст, предос тавляемый классами, использованными для идентификации исклю чений. Как будет показано в следующей главе, исключения, опреде ляемые в программе, могут определяться в виде строк или классов, но в отличие от строк, исключения на базе классов позволяют сценариям создавать категории исключений, наследовать поведение и добавлять к ним информацию о состоянии. Исключения на базе классов более предпочтительны, чем исключения на базе строк, а кроме того, они станут обязательными в Python 3.0: >>> class Bad(Exception): pass >>> def doomed(): raise Bad() >>> try: ... doomed() ... except Bad: ... print 'got Bad' got Bad >>> Обработка исключений: краткий обзор 707 Наконец, инструкции try могут включать блоки finally. Комбинация try /finally определяет завершающие действия, которые всегда выпол няются «на выходе», независимо от того, возникло исключение в бло ке try или нет: >>> try: ... fetcher(x, 3) ... finally: ... print 'after fetch' 'm' after fetch Здесь, если блок try выполнится без ошибок, будет выполнен блок fi nally и программа продолжит свою работу дальше. В этом случае дан ная инструкция кажется бессмысленной – мы могли бы просто доба вить инструкцию print сразу вслед за вызовом функции и вообще уб рать инструкцию try: fetcher(x, 3) print 'after fetch' Однако в таком подходе имеется одна проблема: если в функции воз никнет исключение, инструкция print не будет выполнена. Комбина ция try/finally позволяет ликвидировать эту проблему – когда в блоке try действительно произойдет исключение, блок finally будет выпол нен, пока программа будет раскручиваться: >>> def after(): ... try: ... fetcher(x, 4) ... finally: ... print 'after fetch' ... print 'after try?' >>> after() after fetch Traceback (most recent call last): File " File " File " IndexError: string index out of range (IndexError: выход индекса за пределы строки) Здесь мы не получили сообщение «after try?», потому что работа про граммы не была продолжена после блока try/finally, когда возникло исключение. Вместо этого интерпретатор выполнил действия, преду смотренные блоком finally, после чего исключение достигло предыду щего обработчика (в данном случае – обработчик по умолчанию). Если изменить вызов внутри функции action, чтобы он не вызывал исклю чение, блок finally все равно будет выполнен, но программа продол жит работу после выхода из инструкции try: 708 Глава 27. Основы исключений >>> def after(): ... try: ... fetcher(x, 3) ... finally: ... print 'after fetch' ... print 'after try?' >>> after() after fetch after try? >>> На практике комбинацию try/except удобно использовать для перехвата и восстановления после исключений, а комбинацию try/finally – в слу чаях, когда необходимо гарантировать выполнение заключительных действий независимо от того, возникло исключение в блоке try или нет. Например, комбинацию try/except можно было бы использовать для пе рехвата ошибок, возникающих в импортированной библиотеке, создан ной сторонним разработчиком, а комбинацию try/finally – чтобы га рантировать закрытие файлов и соединений с сервером. Некоторые из таких практических примеров будут показаны далее в этой книге. Несмотря на то, что эти две комбинации служат двум различным це лям, тем не менее, начиная с версии Python 2.5, появилась возмож ность смешивать предложения except и finally в одной и той же инст рукции try – блок finally будет выполняться всегда, независимо от то го, было ли перехвачено исключение предложением except. Такое поведение составляет основу исключений – исключения дейст вительно являются очень простым инструментом. Далее в этой части книги мы подробнее поговорим о самих инструкциях, исследуем раз ные виды предложений, которые могут появляться в инструкции try, и обсудим объекты исключений, основанные на строках и классах. Исключения в языке Python – это высокоуровневый инструмент управ ления потоком выполнения. Они могут возбуждаться интерпретато ром или самой программой – в любом из этих случаев их можно игно рировать (что вызовет срабатывание обработчика по умолчанию) или перехватывать с помощью инструкций try (для обработки в своем про граммном коде). Инструкция try может использоваться в двух логиче ских разновидностях, которые, начиная с версии Python 2.5, могут комбинироваться – одна разновидность выполняет обработку исклю чений, а другая выполняет завершающий программный код независи мо от того, возникло исключение или нет. Исключения можно возбуж дать вручную, с помощью инструкций raise и assert. А теперь, полу чив общее представление, рассмотрим подробнее общие формы упо требления этих инструкций. Инструкция try/except/else 709 Инструкция try/except/else Для начала я представлю try/except/else и try/finally как разные ин струкции, потому что они имеют разное предназначение и не могут комбинироваться в версиях Python ниже, чем 2.5. Как уже говори лось, начиная с этой версии, except и finally могут смешиваться в од ной инструкции try – я объясню суть этого изменения после того, как будут исследованы две оригинальные формы по отдельности. Инструкция try – это составная инструкция. Полная ее форма приво дится ниже. Она начинается со строки заголовка try, вслед за которой располагается блок инструкций (как правило) с отступами, затем сле дует одно или более предложений except, которые определяют пере хватываемые исключения, и затем следует необязательное предложе ние else. Слова try, except и else должны располагаться с одним и тем же отступом (то есть должны быть выровнены по вертикали). Для справки ниже приводится полный формат инструкции: try: except except # и получает дополнительные данные except (name3, name4): except: else: В этой инструкции блок под заголовком try представляет основное дей+ ствие инструкции – программный код, который следует попытаться выполнить. Предложения except определяют обработчики исключе ний, возникших в ходе выполнения блока try, а предложение else (ес ли присутствует) определяет обработчик для случая отсутствия ис ключений. Элемент имеет отношение к особенности инструкций raise , которая будет обсуждаться далее в этой главе. Ниже описывается принцип действия инструкции try. Когда запуска ется инструкция try, интерпретатор помечает текущий контекст про граммы, чтобы вернуться к нему, если возникнет исключение. В пер вую очередь выполняются инструкции, расположенные под заголов ком try. Что произойдет дальше, зависит от того, будет ли возбуждено исключение в блоке try: • Если исключение возникнет во время выполнения инструкций в бло ке try, интерпретатор вернется к инструкции try и выполнит первое предложение except, соответствующее возбужденному исключе нию. После выполнения блока except управление будет передано 710 Глава 27. Основы исключений первой инструкции, находящейся за всей инструкцией try (при ус ловии, что в блоке except не возникло другого исключения). • Если в блоке try возникло исключение, и не было найдено ни одного соответствия среди предложений except, исключение будет переда но инструкции try, стоящей выше в программе, или на верхний уровень процесса (что вынудит интерпретатор аварийно завершить работу программы и вывести сообщение об ошибке по умолчанию). • Если в процессе выполнения блока try не возникло исключение, интерпретатор выполнит инструкции в блоке else (если имеется) и затем выполнение продолжится с первой инструкции, находя щейся за всей инструкцией try. Другими словами, предложения except перехватывают любые исклю чения, которые могут возникнуть при выполнении блока try, а блок else выполняется только в случае отсутствия исключений в блоке try. В предложениях except находятся обработчики исключений – они пе рехватывают исключения, которые возникли только в инструкциях блока try. Однако инструкции в блоке try могут вызывать функции, расположенные в разных частях программы, поэтому сам источник ис ключения может располагаться за пределами самой инструкции try. Мы еще поговорим об этом, когда будем исследовать вложенные инст рукции try в главе 29. Предложения инструкции try В инструкции try могут присутствовать разные предложения, распола гающиеся вслед за блоком try. В табл. 27.1 приводятся все возможные формы, из которых хотя бы одна должна присутствовать. Мы уже встречали некоторые из них: как вы уже знаете, предложение except перехватывает исключения, предложение finally выполняется при вы ходе из инструкции, а предложение else выполняется, когда в блоке try не возникло исключение. С точки зрения синтаксиса, в инструкции может присутствовать несколько предложений except, но только одно предложение else. Вплоть до версии Python 2.4 предложение finally должно быть единственным (без предложений else или except); в дейст вительности это отдельная инструкция. Однако, начиная с версии Py thon 2.5, предложение finally может присутствовать в той же инструк ции, что и предложения except и else. Таблица 27.1. Различные формы предложений в инструкции try Форма предложения Интерпретация except: Перехватывает все (остальные) типы исключений. except name: Перехватывает только указанное исключение. except name, value: Перехватывает указанное исключение и получает со ответствующие ему дополнительные данные (или эк земпляр). Инструкция try/except/else 711 Исследованием дополнительного значения value мы займемся, когда будем рассматривать инструкцию raise. Новыми здесь для нас явля ются первая и четвертая строки в табл. 27.1: • Предложения except, в которых отсутствуют имена исключений (except:), перехватывают все исключения, ранее не перечисленные в инструкции try. • Предложения except, где в круглых скобках перечислены имена ис ключений (except (e1, e2, e3):), перехватывают любое из перечис ленных исключений. Интерпретатор Python просматривает предложения except сверху вниз в поисках соответствия, поэтому версию предложения с круглыми скобками можно рассматривать как аналог нескольким отдельным выражениям except, по одному для каждого исключения из списка, только в этом случае тело обработчика является общим для всех ука занных исключений. Ниже приводится пример использования не скольких предложений except, который демонстрирует порядок опре деления обработчиков: try: action() except NameError: except IndexError except KeyError: except (AttributeError, TypeError, SyntaxError): else: В этом примере, если при выполнении функции action возникает ис ключение, интерпретатор возвращается к инструкции try и пытается отыскать первое предложение except, в котором указано возникшее ис ключение. Поиск среди предложений except ведется сверху вниз, слева направо, и выполняются инструкции в первом найденном совпадении. Если совпадений не будет найдено, исключение продолжит распростра нение выше этой инструкции try. Обратите внимание, что блок else вы полняется только при отсутствии исключения в функции action – except (name1, name2): Перехватывает любое из перечисленных исключений. except (name1, name2), value: Перехватывает любое из перечисленных исключений и получает соответствующие им дополнительные дан ные. else: Выполняется, если не было исключений. finally: Этот блок выполняется всегда. Форма предложения Интерпретация 712 Глава 27. Основы исключений этот блок не выполняется при наличии исключения, которому не было найдено соответствующее предложение except. Если вам действительно необходимо организовать перехват всех ис ключений, используйте пустое предложение: try: action() except NameError: ... # Обработать исключение NameError except IndexError: ... # Обработать исключение IndexError except: ... # Обработать все остальные исключения else: ... # Обработка случая отсутствия исключений Предложение except без имени исключения – это своего рода шаблон ный символ, потому что оно перехватывает любые исключения, что по зволяет вам создавать и универсальные, и специфичные обработчики, по своему усмотрению. В некоторых случаях эта форма может быть бо лее удобна, чем перечисление всех возможных исключений в инструк ции try. Например, в следующем примере выполняется перехват всех исключений: try: action() except: ... # Перехватить все возможные исключения Однако применение пустых предложений except влечет за собой опре деленные проблемы проектирования. Несмотря на удобство, они мо гут перехватывать нежелательные системные исключения, не связан ные с работой вашего программного кода, и по случайности прерывать распространение исключений, предназначенных для других обработ чиков. Например, даже выход из программы в языке Python возбуж дает исключение, и поэтому было бы желательно, чтобы это исключе ние было пропущено. Но пока я скажу лишь, что предложение except требует внимательного отношения. В версии Python 3.0 предполагается изменить форму, представ ленную в третьей строке табл. 27.1: except name, value: планиру ется преобразовать в форму except name as value:. Этим изменени ем предполагается ликвидировать возможность перепутать эту форму со случаем, когда в предложении except указывается кор теж имен исключений – в версии Python 3.0 форма в четвертой строке табл. 27.1 больше не будет требовать наличия круглых скобок. Это изменение также приведет к изменению правил об ласти видимости: с введением новой синтаксической конструк ции as переменная value будет удаляться в конце блока except. Кроме того, в версии 3.0 разновидность инструкции raise E, V должна будет записываться как raise E(V), чтобы явно обозначить Инструкция try/except/else 713 создание класса, который будет играть роль возбуждаемого исклю чения. Предыдущая форма будет сохранена для обратной совмес тимости со строковыми исключениями в Python 2.x. (Далее в этой главе подробнее рассказывается об инструкции raise, а в следую щей главе рассказывается об исключениях на базе классов.) В настоящее время невозможно использовать форму as предложе ния except в Python 2.x, чтобы подготовить свой программный код к грядущим изменениям, однако в составе Python 3.0 будет распространяться инструмент преобразования «2to3», который автоматически будет выполнять преобразования предложения except и инструкции raise в написанном для версии Python 2.x программном коде. Предложение try/else Назначение предложения else в инструкции try на первый взгляд не всегда очевидно для тех, кто только начинает осваивать язык Python. Тем не менее, без этого предложения нет никакого другого способа уз нать (не устанавливая и не проверяя флаги) – выполнение программы продолжилось потому, что исключение в блоке try не было возбужде но, или потому, что исключение было перехвачено и обработано: try: ...выполняемый код... except IndexError: ...обработка исключения... # Программа оказалась здесь потому, что исключение было обработано # или потому, что его не возникло? Точно так же, как предложение else в операторах цикла делает причи ну выхода из цикла более очевидной, предложение else в инструкции try однозначно и очевидно сообщает о произошедшем: try: ...выполняемый код... except IndexError: ...обработка исключения... else: ...исключение не было возбуждено... То же самое поведение можно имитировать, переместив содержимое блока else в блок try: try: ...выполняемый код... ...исключение не было возбуждено... except IndexError: ...обработка исключения... Но это может привести к некорректной классификации исключения. Если какаялибо из инструкций в блоке «исключение не было возбуж дено» приведет к появлению исключения IndexError, оно будет зареги стрировано как ошибка в блоке try и, соответственно, ошибочно будет |