Главная страница
Навигация по странице:

  • Общие формы инструкции raise

  • Закрепление пройденного Контрольные вопросы

  • Вложенные обработчики исключений

  • raise E Рис. 29.1.

  • Пример: вложение в потоке управления

  • raise E Рис. 29.2.

  • Пример: синтаксическое вложение

  • Математический анализ. 3е издание


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница91 из 98
    1   ...   87   88   89   90   91   92   93   94   ...   98
    747
    Как уже упоминалось ранее, исключения на основе классов станут единственно возможными в будущей версии Python. Но даже если бы это было не так, существуют веские основания использовать их. Вооб
    ще говоря, строковые исключения представляют собой простой инст
    румент, предназначенный для решения простых задач. Исключения на основе классов полезны для определения категорий, и они выглядят предпочтительнее в более сложных приложениях, когда можно из
    влечь выгоду из возможности сохранять информацию о состоянии и на
    следовать атрибуты. Не в каждом приложении требуется мощь ООП,
    но преимущества исключений на основе классов становятся более оче
    видными с ростом и расширением программ.
    Общие формы инструкции raise
    С учетом исключений на основе классов инструкция raise может при
    нимать следующие пять форм. Первые две возбуждают строковые ис
    ключения, следующие две – исключения на основе классов, и послед
    няя повторно возбуждает текущее исключение (что очень удобно, ко
    гда необходимо обеспечить дальнейшее распространение произволь
    ных исключений):
    raise string # Соответствует предложению except с тем же объектом
    raise string, data # Передает дополнительные данные (по умолчанию = None)
    raise instance # То же, что и: raise instance.__class__, instance
    raise class, instance # Соответствует предложению except с тем же
    # именем класса или его суперкласса
    raise # Повторно возбуждает текущее исключение
    В наши дни наиболее часто используется третья форма. В случае исклю
    чений на основе классов Python всегда требует указывать экземпляр класса. Возбуждая экземпляр, интерпретатор в действительности воз
    буждает исключение, соответствующее классу экземпляра, а экземпляр передается вместе с исключением в виде элемента дополнительных дан
    ных (как мы уже видели, это отличное место для хранения информа
    ции, которая может использоваться обработчиком). Для обратной со
    вместимости с версиями Python, в которых исключения реализованы на основе строк, можно использовать следующие формы инструкции raise:
    raise class # То же, что и: raise class()
    raise class, arg # То же, что и: raise class(arg)
    raise class, (arg, arg, ...) # То же, что и: raise class(arg, arg, ...)
    Все они соответствуют форме raise class(arg, ...) и, следовательно,
    соответствуют форме raise instance выше. В частности, если вместо эк
    земпляра в инструкции raise указывается класс, и дополнительный элемент данных не является экземпляром указанного класса, интер
    претатор автоматически вызовет класс с дополнительными элемента
    ми данных в качестве аргументов конструктора, чтобы создать и воз
    будить экземпляр класса исключения.

    748
    Глава 28. Объекты исключений
    Например, можно возбудить экземпляр встроенного исключения Key
    Error
    , просто записав инструкцию raise KeyError: даже при том, что
    KeyError в настоящее время является классом, – интерпретатор вызо
    вет KeyError, чтобы создать необходимый экземпляр. Фактически ис
    ключение KeyError и любые другие исключения на основе классов мож
    но возбудить различными способами:
    raise KeyError() # Обычная форма: возбуждается экземпляр
    raise KeyError, KeyError() # Класс, экземпляр: используется экземпляр
    raise KeyError # Класс: будет создан экземпляр
    raise KeyError, "bad spam" # Класс, аргумент: будет создан экземпляр
    Для всех этих пяти форм инструкция try, имеющая следующую форму:
    try:
    except KeyError, X:
    присвоит переменной X объект экземпляра класса KeyError.
    Если это объяснение показалось вам малопонятным, просто запомните,
    что исключения могут идентифицироваться строкой или экземпляром класса. В случае строк вместе с исключением можно передавать допол
    нительные данные. В случае классов, если объект экземпляра не пере
    дается инструкции raise, интерпретатор создаст его автоматически.
    В версии Python 2.5 можно вообще отказаться от использования стро
    ковых форм инструкции raise, потому что при возбуждении строко
    вых исключений генерируются предупреждения, а, кроме того, они будут недоступны в следующей версии Python. Но, к сожалению, об
    ратная совместимость все еще принимается во внимание в книгах, ко
    торые обучают языку программирования, используемому более чем одним миллионом человек!
    В заключение
    В этой главе мы занялись созданием собственных исключений. Здесь мы узнали, что исключения могут быть реализованы как строковые объекты или как экземпляры классов. Однако в настоящее время предпочтение следует отдавать экземплярам классов, а в будущей вер
    сии Python классы станут единственно возможным способом реализа
    ции исключений. Классы исключений предпочтительнее потому, что они поддерживают концепцию создания иерархий исключений (что положительно сказывается на удобстве сопровождения), позволяют присоединять к исключениям дополнительные данные и поведение в виде атрибутов и методов экземпляров, а также обеспечивают насле
    дование атрибутов и методов от суперклассов.
    Мы видели, что перехватывая суперкласс в инструкции try, мы пере
    хватываем этот класс, а также все его подклассы, расположенные ни

    Закрепление пройденного
    749
    же в дереве наследования; суперклассы начинают играть роль назва
    ний категорий, а подклассы становятся определенными типами ис
    ключений в этих категориях. Мы также видели, что инструкция raise поддерживает несколько форматов, хотя большинство современных программ просто создают и возбуждают экземпляры классов.
    В этой главе мы исследовали обе альтернативы – исключения на основе строк и на основе классов. Тем не менее, объекты исключений проще запомнить, если ограничиться только рекомендуемой моделью, осно
    ванной на использовании классов, – описывайте свои исключения как классы, наследуйте класс Exception как вершину своих деревьев исклю
    чений, – и вы сможете забыть старую альтернативу на основе строк.
    Следующая глава завершает эту часть книги и всю книгу в целом ис
    следованием некоторых типичных случаев использования исключе
    ний и рассмотрением инструментов, наиболее часто используемых программистами на языке Python. Однако, прежде чем двинуться дальше, ответьте на контрольные вопросы к этой главе.
    Закрепление пройденного
    Контрольные вопросы
    1. Как определяется соответствие строковых исключений и обработ
    чиков?
    2. Как определяется соответствие исключений на основе классов и об
    работчиков?
    3. Как можно присоединить контекстную информацию к исключени
    ям на основе классов и как ее можно использовать в обработчиках?
    4. Как можно определить текст сообщения об ошибке в исключениях на основе классов?
    5. Почему в настоящее время нежелательно использовать исключе
    ния на основе строк?
    Ответы
    1. Соответствие строковых исключений выявляется по идентичности объекту (технически, с помощью оператора is), а не по значению объекта (оператор ==). Поэтому будет недостаточно использовать то же самое значение – необходимо иметь ссылку на тот же самый объ
    ект (обычно переменную). Короткие строки в языке Python кэши
    руются с целью многократного использования, поэтому использо
    вание одного и того же значения может иногда работать, но вы не должны полагаться на это (подробнее об этой проблеме будет рас
    сказываться в конце следующей главы).
    2. Соответствие исключений на основе классов определяется отноше
    нием к суперклассу: при использовании имени суперкласса в обра

    750
    Глава 28. Объекты исключений ботчике исключения будут перехватываться экземпляры этого класса, а также экземпляры всех его подклассов, расположенных ниже в дереве наследования. Благодаря этому суперклассы можно интерпретировать как категории исключений, а подклассы – как более специфичные типы исключений в этих категориях.
    3. Присоединение дополнительной информации к исключениям на ос
    нове классов производится путем заполнения атрибутов объекта эк
    земпляра исключения, часто внутри конструкторов классов. В обра
    ботчиках исключений указывается переменная, которой присваи
    вается экземпляр исключения, после этого имя переменной может использоваться для доступа к присоединенной информации и для вызова любых унаследованных методов класса.
    4. Текст сообщения об ошибках в исключениях на основе классов мож
    но определить с помощью метода перегрузки __repr__ или __str__.
    Если вы наследуете свои классы от встроенного класса Exception,
    в тексте сообщения автоматически будут отображаться все аргумен
    ты, переданные конструктору.
    5. Потому что, как заявил Гвидо (Guido), в будущей версии Python планируется вообще убрать их. На самом деле, для этого есть весьма серьезные основания: строковые исключения не поддерживают де
    ление на категории, не позволяют присоединять информацию о со
    стоянии или наследовать поведение, как исключения на основе классов. С практической точки зрения строковые исключения про
    ще в использовании на первых порах, пока программы достаточно маленькие, но их становится сложно использовать, как только про
    граммы становятся больше.

    29
    Использование исключений
    Данная глава завершает эту часть книги рассмотрением некоторых тем, связанных с проектированием исключений и примеров их исполь
    зования. Далее следует раздел с описанием типичных проблем и уп
    ражнения. Поскольку эта глава к тому же является последней главой книги, здесь приводится краткий обзор средств разработки, которые помогут вам пройти путь от начинающего программиста до разработ
    чика приложений на языке Python.
    Вложенные обработчики исключений
    До сих пор в наших примерах для перехвата исключений использова
    лась единственная инструкция try, но что произойдет, если одну инст
    рукцию try вложить внутрь другой? И, раз уж на то пошло, что про
    изойдет, если в инструкции try вызывается функция, которая выпол
    няет другую инструкцию try? С технической точки зрения инструк
    ции могут вкладываться друг в друга как синтаксически, так и по пути следования потока управления через программный код.
    Оба эти варианта проще будет понять, если вы узнаете, что интерпре
    татор складывает инструкции try стопкой во время выполнения. Ко
    гда возникает исключение, интерпретатор возвращается к самой по
    следней инструкции try, содержащей соответствующее предложение except
    . Поскольку каждая инструкция try оставляет метку, интерпре
    татор может возвращаться к более ранним инструкциям try, двигаясь по стопке меток. Такое вложение активных обработчиков и есть то,
    что подразумевается, когда мы говорим о распространении исключе
    ний вверх, к обработчикам «более высокого уровня». Эти обработчики являются обычными инструкциями try, в которые поток управления ходом выполнения программы вошел раньше.

    752
    Глава 29. Использование исключений
    Рисунок 29.1 иллюстрирует, что происходит, когда возникает вложе
    ние инструкций try/except во время выполнения. Объем программного кода, который выполняется в инструкции try, может оказаться весьма существенным (например, он может содержать вызовы функций) и не
    редко вызывает другой программный код, который готов перехватить те же самые исключения. Когда исключение наконец возбуждается,
    интерпретатор переходит к самой последней инструкции try, в кото
    рой указано имя исключения, запускает блок except и продолжает вы
    полнение программы ниже этой инструкции try.
    Как только такое исключение будет перехвачено, его жизнь заканчива
    ется – управление не передается всем соответствующим инструкциям try
    , содержащим имя исключения, – только первая из них получает возможность обработать исключение. Например, на рис. 29.1 инструк
    ция raise в функции func2 возвращает управление обработчику в функ
    ции func1, после чего программа продолжает выполнение внутри func1.
    Напротив, когда исключение возникает во вложенных инструкциях try
    /finally, выполняется каждый блок finally по очереди – интерпре
    татор продолжает передавать исключение вверх по цепочке вложен
    ных инструкций try, пока не будет достигнут обработчик по умолча
    нию верхнего уровня (который выводит стандартные сообщения об ошибках). Как показано на рис. 29.2, предложения finally не останав
    ливают распространение исключений – они лишь определяют про
    граммный код, который должен выполняться на выходе из инструк
    ции try в процессе движения исключения. Если к моменту возникно
    вения исключения имелось несколько активных инструкций try/fi
    nally
    , они все будут выполнены, если гдето на пути исключения не встретится инструкция try/except, которая перехватит его.
    Другими словами, куда будет выполнен переход при возникновении исключения, полностью зависит от того, где оно возникло, это опре
    деляется ходом выполнения программы, а не только синтаксисом.
    try:
    func1()
    except E:
    def func1():
    try:
    func2()
    except E:
    def func2():
    raise E
    Рис. 29.1. Вложенные инструкции try/except: когда возбуждается
    исключение (программой или интерпретатором), происходит возврат
    к самой последней инструкции try с соответствующим предложением except
    и программа продолжает выполнение после этой инструкции try.
    Предложения except перехватывают и останавливают дальнейшее
    распространение исключений – это место, где выполняются
    восстановительные операции после исключения

    Вложенные обработчики исключений
    753
    Распространение исключения, по сути, происходит в порядке, обрат
    ном вхождениям в инструкции try. Это движение останавливается,
    когда управление переходит к соответствующему блоку except, и про
    должается когда управление проходит через предложения finally.
    Пример: вложение в потоке управления
    Обратимся к примеру, чтобы рассмотреть этот тип вложения более кон
    кретно. В следующем файле модуля nestexc.py определяются две функ
    ции. Функция action2 возбуждает исключение (нельзя складывать чис
    ла и последовательности), функция action1 обертывает вызов функции action2
    в инструкцию try, которая перехватывает исключение:
    def action2():
    print 1 + [ ] # Возбуждает исключение TypeError
    def action1():
    try:
    action2()
    except TypeError: # Самая последняя соответствующая инструкция try
    print 'inner try'
    try:
    action1()
    except TypeError: # Этот обработчик будет выполнен, только если
    print 'outer try' # action1 повторно возбудит исключение
    % python nestexc.py
    inner try
    Обратите внимание, что на верхнем уровне модуля, внизу файла, вы
    зов функции action1 также обернут инструкцией try. Когда функция action2
    возбуждает исключение TypeError, существует две активные ин
    try:
    func1()
    finally:
    def func1():
    try:
    func2()
    finally:
    def func2():
    raise E
    Рис. 29.2. Вложенные инструкции try/finally: когда возбуждается
    исключение, управление возвращается самой последней инструкции try
    и выполняется ее блок finally, после этого исключение продолжит свое
    распространение по блокам finally во всех активных инструкциях try,
    пока в конечном счете не будет достигнут обработчик по умолчанию,
    где производится вывод сообщения об ошибке. Предложения finally
    перехватывают (но не останавливают) исключения – они определяют
    действия, которые должны выполняться «на выходе»

    754
    Глава 29. Использование исключений струкции try – одна в функции action1 и одна в программном коде на верхнем уровне модуля. Интерпретатор выбирает и запускает самую последнюю инструкцию try с соответствующим предложением except,
    которой в данном случае является инструкция try в функции action1.
    Как уже говорилось, место, куда будет выполнен переход в случае ис
    ключения, зависит от того, в каком месте программы находится поток управления. Поэтому, чтобы знать, куда будет выполнен переход, не
    обходимо знать место, где находится управление. В данном случае вы
    бор места, где будет обработано исключение, больше зависит от того,
    где находится поток управления, чем от синтаксиса. Однако мы мо
    жем организовать синтаксическое вложение обработчиков – эквива
    лентный случай рассматривается в следующем разделе.
    Пример: синтаксическое вложение
    В главе 27, когда рассматривалась новая объединенная инструкция try
    /except/finally, я уже говорил, что вполне возможно вкладывать ин
    струкции try синтаксически, задавая вложение в программном коде:
    try:
    try:
    action2()
    except TypeError: # Самая последняя соответствующая инструкция try
    print 'inner try'
    except TypeError: # Этот обработчик будет выполнен, только если
    print 'outer try' # вложенный обработчик повторно возбудит исключение
    Этот программный код задает ту же структуру вложенных обработчи
    ков, что и предыдущий пример (и ведущую себя точно так же). Факти
    чески инструкции, вложенные синтаксически, работают точно так же, как показано на рис. 29.1 и 29.2; единственное отличие заключа
    ется в том, что вложенные обработчики физически объединены в бло
    ке инструкции try, а не находятся в разных функциях. Например, ис
    ключение пройдет через все блоки finally независимо от того, вложе
    ны они синтаксически или в ходе выполнения программы происходит вложение физически отдельных фрагментов программного кода:
    >>> try:
    ... try:
    ... raise IndexError
    ... finally:
    ... print 'spam'
    ... finally:
    ... print 'SPAM'
    spam
    SPAM
    Traceback (most recent call last):
    File "", line 3, in ?
    IndexError

    Вложенные обработчики исключений
    755
    Графическая иллюстрация порядка выполнения этого фрагмента по
    казана на рис. 29.2 – результат получается тот же самый, но сама ло
    гика выполнения в данном случае образована вложенными инструк
    циями. Более интересный пример синтаксического вложения в дейст
    вии приводится в следующем файле except+finally.py:
    def raise1(): raise IndexError def noraise(): return def raise2(): raise SyntaxError for func in (raise1, noraise, raise2):
    print '\n', func try:
    try:
    func()
    except IndexError:
    print 'caught IndexError'
    finally:
    print 'finally run'
    Этот фрагмент перехватывает исключение, если оно будет возбужде
    но, и выполняет завершающие действия в блоке finally независимо от того, возникло исключение или нет. Чтобы понять это, может потребо
    ваться некоторое время на изучение фрагмента, но результат очень на
    поминает объединение предложений except и finally в одной современ
    ной инструкции try (не забывайте, что такая инструкция была недо
    пустимой до появления версии Python 2.5):
    %
    1   ...   87   88   89   90   91   92   93   94   ...   98


    написать администратору сайта