Лутц М. - Изучаем Python (1077325), страница 161
Текст из файла (страница 161)
Предложения ехсерг перехватывают и останавливают дальнейшее распространение исключений — это место, где выполняются восстановительные операции после исключения Вложенные обработчики исключений рис. 29.2. Вложенные инструкции ггу)впа1! уг когда возбуждается исключение, управление возвращается самой последней инструкции ггу и выполняется ее блок г(паИу, после этого исключение продолжит свое распространение по блокам Лпайу во всех активных инструкциях ггу, пока в конечном счете не будет достигнут обработчик по умолчанию, где производится вывод сообщения об ошибке.
Предложения (таку перехватывают (но не останавливают) исключения — они определяют действия, которые должны выполняться»на выходе» Распространение исключения, по сути, происходит в порядке, обрат- ном вхождениям в инструкции Тгу. Это движение останавливается, когда управление переходит к соответствующему блоку ехсерс, и про- должается когда управление проходит через предложения (1па))у. Пример: вложение в потоке управления Обратимся к примеру, чтобы рассмотреть этот тип вложения более конкретно. В следующем файле модуля пезаехс ру определяются две функции.
Функция асТТоп2 возбуждает исключение (нельзя складывать числа и последовательности), функция ас11оп! обертывает вызов функции ас(!оп2 в инструкцию 1 гу, которая перехватывает исключение; Оет асттоп2(): ртп11+ ( № Возбуидает исключение ТуреЕггог Ое( ар!!оп!(): Тгу. ас(Топ2() ехсерт ТуреЕггог: № Самая последняя соотзетстзуюцая инструкция Ггу рг1т '1ппег Тгу' Тгу ас(1оп1() ехсар( ТуреЕггог: рг!пт 'Оц(Ег Тгу' № Зтот обработчик будет зипояиеи, только если № зстгспз повторно возбудит исключение % ру1аоп пеасехс.ру зппег (гу Обратите внимание, что на верхнем уровне модуля, внизу файла, вызов функции асг(оп! также обернут инструкцией 1гу. Когда функция ас(1оп2 возбуждает исключение Туре Е г го г, существует две активные ин- 754 Глава 29.
Использование исключений струкции сгу — одна в функции ас11оп1 и одна в программном коде на верхнем уровне модуля. Интерпретатор выбирает и запускает самую последнюю инструкцию сгу с соответствующим предложением ехсерг, которой в данном случае является инструкция Тгу в функции асс!оп1. Как уже говорилось, место, куда будет выполнен переход в случае исключения, зависит от того, в каком месте программы находится поток управления. Поэтому, чтобы знать, куда будет выполнен переход, необходимо знать место, где находится управление. В данном случае выбор места, где будет обработано исключение, больше зависит от того, где находится поток управления, чем от синтаксиса, Однако мы можем организовать синтаксическое вложение обработчиков — эквивалентный случай рассматривается в следующем разделе.
Пример: синтаксическое вложение В главе 27, когда рассматривалась новая объединенная инструкция Тгу/ехсерг/т(па11у, я уже говорил, что вполне возможно вкладывать инструкции 1 ту синтаксически, задавая вложение в программном коде: ггу ггу. ас(1оп2() ехсерт турееггог; в саная последняя соответствующая инструкция ггу рг1Ш '1ппег ггу' ехсерт ТуреЕггпг: В Этот обработчик будет випплнвн, только если рыпт 'пцтег тгу' В влпжвнний обработчик повторно возбудит исключение Этот программный код задает ту же структуру вложенных обработчиков, что и предыдущий пример (и ведущую себя точно так же).
Фактически инструкции, вложенные синтаксически, работают точно так же, как показано на рис. 29.1 и 29.2; единственное отличие заключается в том, что вложенные обработчики физически объединены в блоке инструкции сгу, а не находятся в разных функциях. Например, исключение пройдет через все блоки Т1па11у независимо от того, вложены они синтаксически или в ходе выполнения программы происходит вложение физически отдельных фрагментов программного кода: »> тгу: 1гу; га1вв 1пбвхЕггог Г1паыу: рг1п1 'прав' Г1паПУ: рг1пт 'ЗРАМ враз ЗРАМ тгасевасх (щовг гвсепг сап 1ав1): Р11е ?<в(бзп>", 1(пе 3, (п ? 1пбехЕггог Вложенные обработчики исключений 755 Графическая иллюстрация порядка выполнения этого фрагмента показана на рис. 29.2 — результат получается тот же самый, но сама логика выполнения в данном случае образована вложенными инструкциями.
Более интересный пример синтаксического вложения в действии приводится в следующем файле ехсера-7та()у ру: ОеГ га1зе!(): гатзе 1ппехЕггог аег лога!ее(): гетигп Сет га1зе2(); гатза ЗуптахЕггог Гог Гипс 1п (гатзе), пога1зе, гатза2).' рг1п! '<,и', Гипс тгу: тгу.' Гипс() ахсер! 1псехЕггог: рг1п! 'саары 1псехЕггог' Г!па11у; рг1п! 'Гтпа1!у гип' Этот фрагмент перехватывает исключение, если оно будет возбуждено, и выполняет завершающие действия в блоке Г(па11у независимо от того, возникло исключение или нет. Чтобы понять это, может потребоваться некоторое время на изучение фрагмента, но результат очень напоминает объединение предложений ехсерт и (тпа11у в одной современной инструкции !гу (не забывайте, что такая инструкция была недопустимой до появления версии Ру1Ьоп 2.5): % рутпоп ехоарт-Г1па11у. Ру <Гипс!топ гатзе! ат ОхООВА2770> саирж !псахЕггог Гтпа1!у гип <Гипс!топ погатза ат Ох008847РО> 71па11у гип <Гипс!топ га!зе2 ат Ох00884830> Гтпа11у гип Тгасеиаох (аоы гесепт саП 1аз!); Г>1е "С;ггРУтиоп25гехоепт-Г!па11У РУ', 11пе 9, !п <попо!е> Гипс() Рт!е "с;ГРутпоп25гехсерт-!!па!!у.ру", 11пе 3, !и га1зе2 Оег гатзе2(); гаазе ЗуптахЕггог ЗуптахЕггог: Иопе Как мы видели в главе 27, начиная с версии Ру1Ьоп 2.5, появилась возможность использовать предложения ехсер! и Г1па11у в одной инструкции 1гу.
Это делает описанный здесь прием синтаксического вложения ненужным, однако он по-прежнему работает, его можно встретить в программном коде, написанном до выхода версии Ру1Ьоп 2,5, и он может использоваться для реализации альтернативных конструкций обработки исключений. 756 Глава 29. Использование исключений Идиомы исключений Мы рассмотрели внутренний механизм исключений. Теперь рассмот- рим некоторые другие типичные способы их использования. Исключения не всегда являются ошибками В языке РуФоп все ошибки являются исключениями, но не все исключения являются ошибками.
Например, в главе 9 мы видели, что по достижении конца файла метод чтения объекта файла возвращает пустую строку. Напротив, встроенная функция гаи 1лрцс (с которой мы впервые встретились в главе 3 и которую использовали в интерактивном цикле в главе 10) читает по одной строке текста при каждом вызове из стандартного потока ввода зуа. Втб! п и возбуждает исключение ЕОЕЕггог по достижении конца файла. В отличие от методов объекта файла данная функция не возвращает пустую строку; пустая строка, полученная от функции гаи т ерш, означает всего лишь пустую строку. Несмотря на свое название, исключение ЕОЕЕггог в данном контексте — это всего лишь сигнал, а не ошибка.
Но этой причине, чтобы избежать преждевременного завершения работы сценария, функцию гаи 1лрог обертывают инструкцией 1 ту, которую вкладывают в цикл, как показано ниже: иы1Е 1. тту. 1гое = гаи гарце(1 ексерт ЕОЕЕггаг; Ьгеак е1ае а Прочитать строку иа потока атетп Ф Выкод по достикении конца файла обработка следуююей строки. Передача сигналов из функций по условию Исключения, определяемые программой, также могут служить сигналами об условиях, которые не являются ошибками. Например, процедура поиска может предусматривать возбуждение исключения в случае нахождения соответствия вместо того, чтобы возвращать флаг состояния, который должен интерпретироваться вызывающей программой.
В следующем примере инструкция угу/ексерт/е1ае играет роль инструкции ту/е1ее, предназначенной для проверки возвращаемого значения: Существуют и другие встроенные исключения, которые являются сигналами, а не ошибками. В языке РуФЬоп имеется также ряд встроенных исключений, которые являются скорее предупрежденилдги, чем ошибками. Некоторые из них применяются, чтобы сообщить о нежелательности использования некоторых особенностей языка (которые вскоре будут удалены). За дополнительной информацией по предупреждениям обращайтесь к описанию встроенных исключений в руководстве по стандартной библиотеке и к модулю на го таза. Идиомы исключений с1аэа еоопб(ехсерсхоп): рааэ Оег аеагслег(): тг . успех га1ае Еоопс() е1ае: ге1огп 1гу: аеагссег() ехсерг Еоопс: успех е1эе.
.неудача.. № Исключение, если элеиент найден № иначе, элеиент не найде~ с1ааа еат1оге(ехсер11оп): раап оег аеагспег(): г Е ... успех...: ге!ого ...найденный элеиент... е1ае гагэе Еа!1оге() !гу. !!ею = аеагсаег() ехсер! Еаг1оге: .сообиение о неудаче... е1ае: ...обработка найденного элеиента Поскольку язык РуФЬоп является динамически типизированным и в сво- ей основе поддерживает полиморфизм, исключения, а не возвращение специального признака, являются более предпочтительным способом сообщать о таких состояниях.
Отладка с помощью внешних инструкций 1гу Обработчики исключений можно также использовать как замену обработчика по умолчанию. Обернув всю программу (или вызов ее) во внешнюю инструкцию 1 ту, можно перехватывать любые исключения, которые только будут возникать во время работы программы, отменяя тем самым способ завершения программы, заданный по умолчанию. В следующем фрагменте пустое предложение ехсер1 перехватывает любые необработанные исключения, возникшие в ходе выполнения программы. Чтобы получить доступ непосредственно к самому исклю- В более широком смысле такая организация программного кода может с успехом использоваться для любой функции, которая не может вернуть специальный признак, свидетельствующий об успехе или неудаче. Например, если любое возвращаемое значение является допустимым, невозможно выбрать какое-то одно значение, которое сигнализировало бы о необычных состояниях.
Исключения обеспечивают способ подать сигнал, не возвращая значение: 758 Глава 29. Использование исключений чению, вызовите встроенную функцию вув.ехс [п(о из модуля зуав она возвращает кортеж, в котором первые два элемента содержат имя исключения и дополнительные данные (если имеются). Для исключений на основе классов эти два элемента представляют имя класса исключения и экземпляр класса возбужденного исключения соответственно (вскоре мы подробнее рассмотрим функцию вув.
ехс [п То): Тгу: ...запуск программы.. ехсерт № Сюда попадут ясе необработанные исключения 1арогг зуз рг1пг 'опсасрп1!', вуз,ехс 1пто()[0], зув.ехс 1пто()( 1] Этот прием часто используется во время разработки, так как он позволяет сохранить программу активной даже после ошибки — он позволяет производить дополнительные проверки без необходимости перезапускать программу. Это прием может также использоваться для тестирования другого программного кода, как описано в следующем разделе.