Неужели не нашлось русских специалистов, чтобы записать курс по пайтону ? Да, можно включить переводчик и слушать с переводом, но это что? Это кто-то считает хорошим и понятным курсом для начинающих? |
Программное возбуждение исключений. Коллективная обработка исключений
Смотреть на youtube
Когда мы работаем с собственным классом и спроектированным для него классом исключений, то, понятно, исключение может появиться только в результате выполнения кода, написанного программистом. Обнаружить ситуацию, приводящую к некорректной работе класса и его объектов, создать в этом случае объект исключение, передав ему соответствующие аргументы, - все это выполняется обычными программными средствами, ведь исключение это такой же объект, как и другие объекты, с которыми работает программист. Но для того чтобы реально возникла исключительная ситуация, прерывающая выполнение программного кода, необходим в этом случае специальный оператор, который "возбуждает" исключение. Иногда говорят, что оператор "поднимает" или " выбрасывает" исключение. Таким оператором в языке Python является оператор raise. Синтаксис оператора имеет вид:
raise [<expression1> [from <expression2>] ]
Рассмотрим семантику оператора:
- Если выражение отсутствует и задано только ключевое слово raise, то возбуждается последнее исключение, активное в области действия оператора raise. Если активного исключения нет, то возбуждается исключение класса RuntimeError.
- Выражение1 должно быть именем класса или объектом класса. Класс, конечно, должен быть классом исключений - потомок класса BaseException. В этом случае возбуждается исключение, тип которого определяется заданным классом, а значением соответствующий объект. Если в операторе указан класс, а не объект, то объект создается автоматически конструктором по умолчанию. Для собственных классов исключений, имеющих кортеж аргументов, при возбуждении исключения соответствующий объект следует создать.
- Если в операторе raise помимо выражения1 указывается и from выражение 2, то это выражение также задает класс исключений или объект исключений. Такое исключение присоединяется к исключению, заданному в выражении1. Эта редко используемая форма применяется обычно в except-обработчиках, когда обработчик в ходе своей работы возбуждает собственное исключение, присоединяя к нему исключение, захваченное обработчиком.
Приведу пример, где обработчик исключения захватывает любое исключение. В процессе обработки выбрасывает исключение RuntimeError, присоединяя к нему захваченное исключение:
def ff(): try: x = 1 y = [2] z = x + y except Exception as except_obj: raise RuntimeError("Серьезная ошибка при выполнении ff") from except_obj
Вот как выглядит информация, выдаваемая при запуске этой функции:
С другими примерами применения оператора raise мы сталкивались в предыдущих лекциях, в частности, при описании методов класса Rational возбуждалось исключение класса TypeError.
Обработка исключений с подъемом по цепочке вызовов методов
Понимание того, как обрабатывается исключительная ситуация, требует более серьезного рассмотрения. До сих пор неявно предполагалось, что исключительная ситуация возбуждается в охраняемом блоке и один из except-обработчиков этого блока выполняет обработку этой ситуации. Однако зачастую в обработке возникшей ситуации участвует ансамбль обработчиков разных охраняемых блоков. Дело в том, что в теле охраняемого блока метода f1 может содержаться вызов метода f2, в свою очередь содержащего охраняемый блок с соответствующими except-обработчиками. Транзитивно ситуация может повторяться. Предположим, что исключительная ситуация возникла в методе fk. Понятно, что возлагать всю ответственность за обработку ситуации только на except-обработчиков блока fk неразумно, - обработчики всех блоков должны принять участие в обработке, главная ответственность лежит на except-обработчиках блока f1 - метода, породившего цепочку вызовов, приведшую к появлению исключительной ситуации.
Как строится обработка исключения в таких случаях? Первым ситуацию пытается исправить except-обработчик того блока, в котором возникла ситуация. Заканчивает он свою работу вызовом оператора raise, возбуждая активную исключительную ситуацию. В результате происходит подъем по цепочке вызовов методов и в исправление ситуации принимает очередной разработчик. На каком-то шаге ситуация может быть исправлена, исключительная ситуация не возбуждается и продолжается нормальное выполнение. Другой исход - завершение работы, когда становится понятно, что дальнейшее продолжение работы становится невозможным. Чаще всего, окончательное решение принимается при обработке ситуации в вызывающем методе f1.
Давайте вернемся к выше приведенному примеру, где подобная ситуация имела место, хотя я не акцентировал на этом внимания. Напомню суть примера. В тесте вызывалась функция fg, в теле которой был охраняемый блок с except-обработчиками. При выполнении этой функции могли возникать исключительные ситуации двух типов. Ситуация деления на ноль возникала непосредственно в охраняемом блоке функции fg и except-обработчик этого блока исправлял ситуацию, продолжая выполнение программы. Вторая же ситуация, связанная с некорректностью типа операнда, возникала при вызове функции f. У этой функции не было охраняемого блока и соответственно except-обработчиков. Поэтому при возникновении исключительной ситуации был вызван стандартный обработчик исключения, который исправить ситуацию не в состоянии, завершить выполнение программы не вправе, он обязан передать управление вышестоящему except-обработчику, что и было сделано. Обработчик охраняемого блока функции fg смог исправить ситуацию и выполнение программы продолжилось.
Сейчас я построю более сложный пример, проясняющий детали обработки исключения при наличии цепочки вызовов методов в теле охраняемых блоков.