НОЧУ ДПО "Национальный открытый университет "ИНТУИТ"
Опубликован: 24.01.2021 | Доступ: свободный | Студентов: 1229 / 21 | Длительность: 03:57:00
Лекция 28:

Программное возбуждение исключений. Коллективная обработка исключений

< Лекция 1 || Лекция 28: 12

Смотреть на 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 смог исправить ситуацию и выполнение программы продолжилось.

Сейчас я построю более сложный пример, проясняющий детали обработки исключения при наличии цепочки вызовов методов в теле охраняемых блоков.

< Лекция 1 || Лекция 28: 12
Елена Лаптева
Елена Лаптева

Думаю. что не смогу его закончить. Хотелось предупредить других - не тратьте зря время, ищите другой курс.

Михаил Сидоров
Михаил Сидоров

Если S - последовательность, то срез задается как S(i : j) и содержит j - i элементов,

а в примере используютс другие скобки - 

NL[1:3] = ["решили", "не", "искать"]

или это не срез, тогда, что это?