[МУЗЫКА] [МУЗЫКА] Итак,
нам нужно сообщать пользователю какую-то более подробную информацию о том,
что он сделал не так, чтобы он мог у себя взять и исправить эту ошибку.
К сожалению, сами исправить эту ошибку мы не можем, потому что мы не знаем,
в результате чего строка умножилась на комплексное число.
Может быть, вы написали какой-то веб-сайт, где пользователи могут ввести вещественные
числа и посчитать их произведение.
И какой-то злой пользователь или кто-то там, кто испытывает судьбу,
взял и написал туда строку.
Тогда вам нужно в этой ситуации пользователю сообщить,
что вообще-то нет, здесь нужно писать только числа.
Или там совершенно в другом контексте вы как-то умножили на строку,
вам нужно разобраться, как так получилось,
но все это не относится к компетенции вашего класса комплексного числа.
Оно не знает, откуда взялись его параметры, оно знает,
что они неправильные, и должно об этом сообщить.
Для этого есть механизм исключений.
Что такое исключение?
Это какая-то ошибочная ситуация, которая вообще в принципе может быть обработана,
то есть она не должна сразу взять и завалить вашу программу,
но не на том уровне логики, на котором вы находитесь.
То есть на более внешнем каком-то уровне, может быть, на интерфейсе пользователя.
Вы поняли, что у вас где-то в глубине вашей программы произошла ошибка,
но она возникла в результате того, что пользователь ввел что-то не то.
Ну, ломаться не нужно, нужно просто попросить пользователя ввести заново,
например.
Итак, как же делаются наши ошибки.
Для ошибок у нас используется класс.
То есть что происходит, когда мы выбрасываем исключения?
У нас прерывается работа всех запущенных функций, которые расположены в стеке,
до тех пор, пока мы не наткнемся на обработчика ошибок.
А этот обработчик ошибок поймает, собственно,
объект класса ошибочного.
Собственно, это обычный класс, который мы сейчас создадим.
Что нам требуется от этого класса?
Нам нужно, чтобы он хранил информацию, собственно, об ошибке,
чтобы было понятно, что вывести пользователю, как ее исправлять.
У него должен быть свой конструктор.
То есть сейчас мы создаем новый класс,
у него есть свой конструктор, у него есть self.
Что нам нужно сообщить пользователю,
чтобы он смог быстро понять, где у него возникла ошибка, и как-то ее обработать,
исправить свой код, или написать обработчик в интерфейсной части?
Нам нужно сообщить оба множителя.
Ошибка будет возникать при умножении комплексного числа на что-то плохое,
на что мы не умеем умножать комплексное число.
Так давайте сообщим ему и комплексное число, с которым возникла эта проблема,
ну потому что у пользователя их может быть много, по значению он быстрее его найдет,
и, собственно, на что он пытался умножить — так он тоже быстрее поймет,
откуда у него это взялось, может быть, увидит что-то знакомое, поймет,
что опечатался и умножил случайно не ту переменную, перепутал название.
То есть в общем-то нам нужно два параметра: первое для комплексного числа,
второе на что оно умножалось.
Куда мы это будем складывать?
Давайте просто в структуре создадим поля.
Ну давайте назову их first и second с таким намеком
на универсальный обработчик чего угодно.
Давайте я лучше назову это класс ComplexError,
чтобы уж совсем все было понятно, к чему он относится.
Итак, вот он наш первый аргумент,
вот наш второй аргумент.
Мы их разложили аккуратно в поля, и теперь все должно быть хорошо.
Теперь нам нужно в какой-то момент сконструировать
объект класса Error, ошибку, записать в него нужную информацию,
при каких условиях эта ошибка возникла, и вызвать исключение, которое
как раз будет распутывать наш стек, пока мы не натолкнемся на обработчик.
Где это нужно сделать?
В нашей функции умножения у нас описан if и elif.
Для комплексных чисел, для целых вещественных.
Во всех остальных случаях мы не знаем, что делать.
Пишем else и что делаем:
создаем объект ошибки, конструируем его.
С какими параметрами?
Параметры мы договорились, что у нас будет комплексное число,
а затем, на что оно пыталось умножиться.
То есть, по сути, сейчас это self комплексное число,
и other, на что мы пытались умножить.
Вот таким образом он сконструировался,
и [БЕЗ СЛОВ] исключения мы бросаем командой raise.
Вызвать исключения.
Собственно, указывается, какой класс мы хотим отправить в обработчик.
Вот этот самый класс с описанием ошибки.
После этого работа метода прерывается, ну и если он был вызван из каких-то функций и
так далее, и так далее, оно выходит, выходит, выходит до момента обработчика.
Давайте напишем обработчик.
Обработчик пишется следующим образом.
Сначала пишется слово try — попробуй.
Затем блок действий, которые надо попробовать сделать.
И после этого пишется слово except.
Except — это если не получилось.
То есть что делать, если у нас в процессе выполнения команд в try
возникла какая-то ошибка, какое-то исключение было брошено.
Ну вот, в принципе здесь мы можем...
Как бы это сделал плохой программист?
Давайте сделаем.
Напишем, что возникла ошибка.
Вот, отлично!
Наверняка вы сталкивались с такой проблемой, что вы заполняете на сайте,
например, какую-то форму, нажимаете «Отправить», а оно говорит: «Ошибка».
И вы сидите ломаете голову, что за ошибка, что же оно хотело.
И там оказывается, что телефон нужно было написать...
вместо 8 написать +7, или какие-то скобочки или еще что-то.
Догадаться до этого невозможно, и так поступают только плохие программисты.
Мы пытаемся стать хорошими.
Давайте пытаться лучше.
Итак, у except кроме вот такой примитивной формы, что какая-то ошибка возникла,
и тогда делай что-то, есть более совершенные формы.
А именно, можно указать класс ошибок, которые мы будем ловить этим except'ом.
В принципе, у вас может быть несколько except'ов.
То есть если у вас может возникать несколько разных типов ошибок,
выбрасываться несколько разных исключений, то вы можете на каждое последовательно
написать свой except, и будет вызываться именно тот, какой класс вы выбросите.
Если же вы используете наследование ошибок от ошибок,
то у вас можно объединять некоторое
поддерево этого наследования в родительский класс.
Ну у нас он всего один, поэтому нам это пока не нужно.
Итак, мы пишем название класса, потом слово as («как») и название переменной.
То есть теперь вот эта переменная CE, сокращение от ComplexError,
будет объектом класса ComplexError,
и мы сможем напечатать ну что-нибудь такое,
что наведет нашего программиста, который пользуется нашим классом,
или пользователя, который все-таки увидел эту ошибку,
хотя он не программист, но может из этого сделать какой-то вывод,
что он сделал не так.
Что мы для этого сделаем?
Мы напишем, что эта ошибка возникла в процессе умножения.
Первый параметр был CE first,
ComplexError first, то, что мы сохранили, у нас напечатается комплексное число.
Второй параметр...
Длинно получается, но ничего страшного.
Давайте я разобью на две строки, чтобы было совсем...
Много букв, но зато все-все-все ясно и очевидно.
Ну если оно заработает, конечно.
Давайте посмотрим.
Вот здесь запятой не хватает.
Все.
Поехали.
Ага.
Вот смотрите.
У нас возникла ошибка.
Как это переводится?
Ловить классы, которые не являются наследниками base exception нельзя.
То есть мы не можем просто взять и создать какой-то класс и его ловить Try catch'ем.
Ну исправляется эта ошибка очень легко.
После имени класса в скобках круглых указывается, от кого он наследуется.
Вот он говорит, от base exception — отлично.
Унаследован от base exception.
И все. И больше мы ничего не меняем, надеемся,
что все будет работать, как мы хотим.
Да, вот у нас наша диагностика.
Ну не очень красиво, у меня пробелы перед запятыми, это я легко исправлю сейчас,
но, тем не менее, смысл понятен.
У нас возникла ошибка в процессе умножения,
первый параметр понятно какой, второй параметр — тоже понятно какой.
Пользователь смотрит, думает, чего здесь написано, пытается понять, смотрит: «Ой!
А я же действительно умножил строку на комплексное число».
Исправляет эту ошибку, запускает...
Запускает еще раз — и теперь все хорошо.
Никакой ошибки нет, выводится результат операции умножения,
и этот блок except не вызывается.
Таким образом вы можете обрабатывать разные исключительные ситуации.
Это не совсем ошибка.
То есть если ваша программа может не сломаться, если есть шанс,
что на каком-то более верхнем слое логики во взаимодействии с пользователем,
например, вы можете эту ошибку как-то исправить, или, например,
попытаться еще раз умножить, вдруг со второй попытки получится, так бывает,
например, при доступе к файлам или каким-то ресурсам, которые заняты были,
а через некоторое время попробовали, они уже свободны.
В таких ситуациях имеет смысл бросать исключения.
Конечно, исключение бросается очень долго,
это тяжелая операция распутывания стека, но в некоторых ситуациях это имеет смысл,
чтобы ваша программа просто не обвалилась молча, не перестала работать.
Ну и диагностику, конечно, нужно делать как можно более подробную,
чтобы сразу человек взглянул, одним взглядом посмотрел и понял,
где у него ошибка, и исправил ее.
[МУЗЫКА]
[МУЗЫКА]