Вопросы:
· Определение исключений.
· Обработка исключений.
Итак, рассмотрим одну из программ, которую мы описали ранее. Это программа для решения квадратных уравнений.
print ('Программа для решения квадратных уравнений.\nВведите коэффициенты уравнения.')
a, b, c = float (input ()), float (input ()), float (input ())
D = b ** 2 - 4 * a * c
from math import sqrt
if D < 0:
print ('Заданное уравнение не имеет решений.')
elif D == 0:
x = -b / (2 * a)
print ('x =', x)
else:
x1 = (-b + sqrt (D)) / (2 * a)
x2 = (-b - sqrt (D)) / (2 * a)
print ('x1 =', x1, 'x2 =', x2)
Запустим её на выполнение. Сразу попробуем ввести в качестве значения одного из коэффициентов произвольную символьную строку. Исполнение программы завершилось ошибкой.
Сегодня мы изменим эту программу таким образом, чтобы её исполнение завершалось корректно и независимо от введённых данных.
Многие из вас наверняка подумали о том, чтобы при вводе данных проверять, является ли введённое значение числом. Ранее мы даже описали функцию IsNumber для этого. Однако логика работы этой функции достаточно сложна. Для того, чтобы устранить возможность ошибки, мы можем использовать куда более простой инструмент, который реализован в большинстве современных языков программирования. Он называется обработчиком исключений.
Что же такое исключения? Это любые ошибки, которые могут возникнуть при исполнении программы. Исключения разделены на категории, которые образуют целую иерархию. Любые действия программы, которых программист не ожидает, в том числе и необработанные исключения, называют багами, от английского слова «bug», что в переводе означает «жучок». Изначально этим словом называли любые неполадки в работе радиоэлектронной аппаратуры. В тысяча девятьсот сорок шестом году учёный-информатик Грейс Хоппер, работая за компьютером Mark-2, заметила неполадки в его работе. Отследив ошибку до одного из электрических реле компьютера, она обнаружила сгоревшего мотылька, который замыкал контакты, что вызывало неполадки в работе. Это был первый задокументированный случай, когда был найден реальный жучок. Сейчас словом «баг» чаще всего называют именно программные ошибки.
Вернёмся к окну среды разработки, в котором было выведено сообщение об ошибке. Эта короткая запись содержит полную информацию о возникшем исключении: адрес и имя модуля, в котором возникла ошибка, строку модуля, при исполнении которой возникла ошибка, а также тип этой ошибки. Это сообщение выводится только в том случае, если возникшее исключение никак не обрабатывается и призвано сообщить программисту о том, что при исполнении кода программы произошло исключение, которое он не предусмотрел.
Рассмотрим запись обработки исключений в языке Python. Она начинается с блока try, что в переводе означает «попытка». В этом блоке записывается код, исполнение которого может вызвать исключение. Если при исполнении этого кода на каком-то шаге будет вызвана ошибка, то исполнение этого блока будет прервано. После блока try следует блок except. Этот блок кода выполняется только в том случае, если при исполнении блока try было вызвано исключение. Однако такой блок будет выполняться независимо от типа исключения, возникшего в блоке try. Поэтому на практике при описании блока except указывается тип исключения, которое этот блок обрабатывает. Также для одного блока try может следовать несколько блоков except, каждый из которых обрабатывает свой тип исключений.
Чаще всего встречаются три типа исключений: деление на ноль – ZeroDivisionError, ошибочный литерал – ValueError и ошибка несоответствия типов – TypeError. С первым типом исключений всё понятно из названия. Такое исключение возникает при попытке деления любого значения на ноль. Исключение типа ValueError возникает при попытке передачи некорректного литерала, как, например, при попытке преобразовать в число символьную строку, содержащую буквы или другие некорректные символы. Исключение типа TypeError возникает при попытке выполнить одну из операций над объектом несоответствующего типа, например, при попытке извлечь квадратный корень из символьной строки. Есть и другие типы исключений. Они встречаются не так часто, и при желании вы можете прочесть о них самостоятельно.
Итак, перейдём к изменению нашей программы. Изменим ввод данных в программе. Для этого опишем функцию для считывания числа. Опишем её в отдельном модуле, в котором будем сохранять функции ввода - stdIn. Назовём нашу функцию getFloat. У неё не будет параметров. В теле функции напишем бесконечный цикл. В цикле в блоке try запишем инструкцию возврата значения, считанного с клавиатуры, преобразованного в вещественное число. Ошибку в этой инструкции может вернуть функция float, так как пользователь может ввести не только число. Поэтому запишем блок except для исключения типа ValueError, то есть для некорректного литерала. В этом блоке запишем инструкцию print, которая будет выводить на экран сообщение о том, что введённое значение не является числом, и запрос на повторный ввод. Описание этой функции завершено. Сохраним этот модуль в одной папке с модулем для решения квадратных уравнений.
def getFloat ():
while True:
try:
return float (input ())
except:
print ('Введённое значение не является числом. Повторте ввод.')
Теперь в модуле для решения квадратных уравнений уберём инструкцию, следующую во второй строке, и на её месте загрузим из модуля stdIn описанную функцию getFloat. Дальше с помощью инструкции print выведем на экран запрос на ввод коэффициента a без перехода на следующую строку. Теперь присвоим переменной a значение функции getFloat. Дважды скопируем инструкции для считывания A, после чего изменим их для считывания значений коэффициентов b и c. Теперь программа не должна возвращать сообщение об ошибке, если пользователь введёт что-нибудь кроме чисел.
print ('Программа для решения квадратных уравнений. Введите коэффициенты уравнения.')
from StdIn import GetFloat
print ('A = ', end = '')
a = GetFloat ()
print ('B = ', end = '')
b = GetFloat ()
print ('C = ', end = '')
c = GetFloat ()
D = b ** 2 - 4 * a * c
from math import sqrt
if D < 0:
print ('Заданное уравнение не имеет решений.')
elif D == 0:
x = -b / (2 * a)
print ('x =', x)
else:
x1 = (-b + sqrt (D)) / (2 * a)
x2 = (-b - sqrt (D)) / (2 * a)
print ('x1 =', x1, 'x2 =', x2)
Запустим написанную программу на выполнение. Попробуем ввести в качестве значения коэффициента A произвольную символьную строку. Было выведено сообщение о некорректном вводе и запрос на повторный ввод. Теперь попробуем задать коэффициенты уравнения, равными, соответственно 1, -5 и 4. Заданное уравнение действительно имеет решения, равные 4 и 1. Снова запустим программу и попробуем задать коэффициенты уравнения равными 0, 5 и 7. Исполнение программы завершилось ошибкой деления на 0 при попытке исполнения кода в 17 строке модуля.
Просмотрим 17 строку модуля. Чтобы её найти, при перемещении курсора будем смотреть на строку состояния окна среды разработки. В ней указан номер строки и номер символьной позиции, на которую указывает курсор в данный момент. Установив курсор в 17 строку. Мы видим, что в ней значение делится на 2*a. На самом деле уравнение не является квадратным, если A = 0. Поэтому заключим всё ветвление в блок try и допишем после него блок except для исключения типа ZeroDivisionError. В нём напишем инструкцию print, которая выводит на экран сообщение о том, что заданное уравнение не является квадратным, так как в нём A = 0.
print ('Программа для решения квадратных уравнений. Введите коэффициенты уравнения.')
from StdIn import GetFloat
print ('A = ', end = '')
a = GetFloat ()
print ('B = ', end = '')
b = GetFloat ()
print ('C = ', end = '')
c = GetFloat ()
D = b ** 2 - 4 * a * c
from math import sqrt
try:
if D < 0:
print ('Заданное уравнение не имеет решений.')
elif D == 0:
x = -b / (2 * a)
print ('x =', x)
else:
x1 = (-b + sqrt (D)) / (2 * a)
x2 = (-b - sqrt (D)) / (2 * a)
print ('x1 =', x1, 'x2 =', x2)
except ZeroDivisionError:
print ('Заданное уравнение не является квадратным, т. к. в нём А = 0.')
Сохраним изменённую программу и запустим её на выполнение. Зададим коэффициенты уравнения, как и при последней удачной попытке, равными 1, -5 и 4. Результат исполнения программы не изменился. Теперь снова попробуем задать A = 0. Программа вывела сообщение о том, что заданное уравнение не является квадратным, так как его коэффициент A = 0. Программа работает правильно, и теперь какие бы данные мы не ввели, её исполнение завершится корректно.
При описании обработки исключений после блоков except может следовать ещё один или два необязательных блока. Блок else содержит код, который выполняется в том случае, если при исполнении блока ty не возникло исключений. Блок finally содержит код, который будет выполнен в любом случае, независимо от того, возникли ли исключения.
Мы узнали:
· Исключениями называются любые ошибки, которые происходят в ходе исполнения программы. Они делятся на категории, образующие иерархию.
· На практике часто встречаются три типа исключений: деление на ноль – ZeroDivisionError, ошибочный литерал – ValueError и ошибка несоответствия типов – TypeError.