Исключения exception
— это средство выхода из нормального потока управления блоком кода для обработки ошибок или других исключительных условий. Исключение поднимается в точке обнаружения ошибки. Оно может быть обработано окружающим блоком кода или любым блоком кода, который прямо или косвенно вызвал исключение exception
.
Интерпретатор Python создает исключение, когда обнаруживает ошибку времени выполнения, например, деление на ноль. Программа Python также может явно вызвать исключение с помощью инструкции raise
. Обработчики исключений указываются с помощью оператора try ... except
. Предложение finally
такого оператора можно использовать для указания кода очистки, который не обрабатывает исключение, но выполняется независимо от того, произошло ли исключение в предыдущем коде.
Python использует модель «завершения» обработки ошибок: обработчик исключений может выяснить, что произошло, и продолжить выполнение на внешнем уровне, но он не может устранить причину ошибки и повторить неудачную операцию (за исключением повторного ввода нарушающего фрагмента кода сверху).
Когда исключение вообще не обрабатывается, интерпретатор прекращает выполнение программы или возвращается к своему интерактивному основному циклу. В любом случае он печатает трассировку стека, кроме случаев, когда поднимается исключение SystemExit
.
Исключения определяются экземплярами классов. Предложение except
выбирается в зависимости от класса экземпляра: оно должно ссылаться на класс экземпляра или его базовый класс. Экземпляр может быть получен обработчиком и может содержать дополнительную информацию об исключительном состоянии.
:
Сообщения об исключениях не являются частью Python API. Их содержимое может меняться от одной версии Python к другой без предупреждения и не должно зависеть от кода, который будет работать под несколькими версиями интерпретатора.
Создание программного обеспечения — сложный процесс, осуществляемый коллективом специалистов (реже — одним человеком), а людям свойственно допускать ошибки (человеческий фактор). В связи с этим, код необходимо писать так, чтобы сводить возможные ошибки к минимуму, а также иметь возможность их эффективно определять, искать и обрабатывать, что является одним из признаков качественного программного обеспечения.
Содержание
-
Известные ошибки в ПО
-
1962 г.: ракета Маринер-1
-
1985 г.: аппарат лучевой терапии Therac-25
-
1991 г.: ЗРК Patriot
-
2000 г.: Проблема 2000 года (Y2K)
-
2009-2011 г.: отзыв автомобилей Toyota
-
-
Определение и разновидности ошибок
-
Синтаксические ошибки
-
Логические (семантические) ошибки
-
Ошибки времени выполнения
-
Недокументированное поведение
-
-
Поиск ошибок и отладка программы
-
Подходы к обработке ошибок
-
Обработка исключений в Python
-
Понятия исключения
-
Конструкция
try
-
Возбуждение исключений (
raise
) -
Особенности обработки исключений внутри функций
-
Утверждения (
assert
) -
Исключения или утверждения?
-
-
Рекомендации
7.1.1. Известные ошибки в ПО¶
История знает множество примеров, где программные ошибки стоили не только огромных денег, но и человеческих жизней 6 7:
7.1.1.1. 1962 г.: ракета Маринер-1¶
Маринер-1 — космический аппарат США для изучения Венеры (Рисунок 7.1.1).
Рисунок 7.1.1 — Ракета Маринер-1 9¶
-
Описание и причина:
Программист сделал ошибку, когда переводил рукописные математические формулы в код. Символ логического отрицания ¬ он принял за минус, и это привело к тому, что ракета воспринимала нормальные скорости как критические и из-за этого сбилась с курса.
-
Примерный ущерб / потери
Никто не погиб, однако экономические потери составили 18,3 млн. долларов.
7.1.1.2. 1985 г.: аппарат лучевой терапии Therac-25¶
Therac-25 — канадский аппарат лучевой терапии (Рисунок 7.1.2).
Рисунок 7.1.2 — Аппарат лучевой терапии `Therac-25 10¶
-
Описание и причина:
Неисправность была вызвана тем, что в проекте использовались библиотеки с ошибками, входящие в состав ПО аппарата Therac-20, что и привело к фатальным последствиям. В коде была найдена довольно распространенная ошибка многопоточности, называемое состоянием гонки. Тем не менее ошибку не заметили, так как Therac-20 работал исправно из-за дополнительных (аппаратных) мер предосторожности.
-
Примерный ущерб / потери
Умерло 2 человека, 4 получили серьезное облучение.
7.1.1.3. 1991 г.: ЗРК Patriot¶
Patriot — американский зенитный ракетный комплекс (Рисунок 7.1.3)
Рисунок 7.1.3 — ЗРК Patriot 11¶
-
Описание и причина:
Во время Войны в Персидском заливе по казармам подразделений США был нанесен ракетный удар иракскими ракетами типа Р-17 (советская баллистическая ракета). Ни одна из ракет не была перехвачена, и удар достиг цели.
В программном обеспечении ЗРК, отвечающем за ведение и перехват цели, присутствовала ошибка, из-за которой со временем внутренние часы постепенно отходили от истинного значения времени: системное время хранилось как целое число в 24-битном регистре с точностью до 0,1 секунды; при итоговом расчете данные переводились в вещественное число.
Проблема заключалась в том, что число (cfrac{1}{10}) не имеет точного представления в двоичной системе счисления:
-
(cfrac{1}{10}_{10} = 0,0001100110011001100110011001100…_{2});
-
(cfrac{1}{10}_{10} = 0,00011001100110011001100_{2}) (24-битное целое в системе Patriot);
-
ошибка 1 измерения: ~ (0,000000095_{10});
-
ошибка за 100 часов работы (0,000000095 cdot 10 cdot 60 cdot 60 cdot 100 = 0,34) с.;
-
ракета Р-17 летит со скоростью 1676 м/c, и проходит за 0,34 с. больше полукилометра.
Данной ошибки в измерениях было достаточно, чтобы ракета преодолела радиус поражения Patriot (Рисунок 7.1.4, Видео 7.1.1).
Рисунок 7.1.4 — Неправильное определение зоны пролета ракеты 12¶
Видео 7.1.1 — Демонстрация ошибки ПО Patriot
-
-
Примерный ущерб / потери
Погибло 28 американских солдат и еще двести получили ранения.
7.1.1.4. 2000 г.: Проблема 2000 года (Y2K)¶
-
Описание и причина:
Разработчики программного обеспечения, выпущенного в XX веке, зачастую использовали два знака для представления года в датах: например, 1 января 1961 года представлялось как «01.01.61». При наступлении 1 января 2000 года при двузначном представлении года после 99 наступал 00 год (т.е. 99 + 1 = 00), что интерпретировалось многими старыми программами как 1900 год. Сложность была еще и в том, что многие программы обращались к вычислению дат вперед (например, при составлении плана закупок, планировании даты полета и т.д.) (Рисунок 7.1.5).
Рисунок 7.1.5 — Табло показывает 3 января 1900 года, вместо 3 января 2000 года. Франция 13¶
-
Примерный ущерб / потери
30-300 млрд. долларов.
7.1.1.5. 2009-2011 г.: отзыв автомобилей Toyota¶
-
Описание и причина:
Неисправность в дроссельной заслонке (регулирует количество горючей смеси, поступающей в цилиндры двигателя внутреннего сгорания) с электронным контролем (ETC) приводила к случайным ускорениям автомобиля (Видео 7.1.2).
Видео 7.1.2 — Случайное ускорение автомобиля
В ходе десятимесячного расследования специалисты NASA выявили, что программное обеспечение не соответствует стандартам MISRA (англ. Motor Industry Software Reliability Association) и содержит 7134 нарушения. Представители Toyota ответили, что у них свои собственные стандарты.
20 декабря 2010 года Тойота отвергнула обвинения, но выплатила 16 млрд. долларов в досудебном порядке по искам, выпустила обновление ПО для некоторых моделей машин и отозвала 5,5 млн. автомобилей 8 (Рисунок 7.1.6).
Рисунок 7.1.6 — Lexus ES 350 2007-2010 — одна из моделей с неисправностью 14¶
-
Примерный ущерб / потери
Погибло не менее 89 человек, многомиллиардные потери компании.
7.1.2. Определение и разновидности ошибок¶
Ошибка (также баг от англ. Software Bug) — неполадка в программе, из-за которой она ведет себя неопределенно, выдавая неожиданный результат.
Основные категории ошибок:
-
синтаксические;
-
логические;
-
ошибки времени выполнения;
-
недокументированное поведение.
7.1.2.1. Синтаксические ошибки¶
-
Причина:
Несоответствие синтаксису языка программирования. Для компилируемых языков программирования синтаксическая ошибка не позволит выполнить компиляцию.
-
Пример:
>>> for i in range(10) File "<stdin>", line 1 for i in range(10) ^ SyntaxError: invalid syntax
7.1.2.2. Логические (семантические) ошибки¶
-
Причина:
Несоответствие правильной логике работы программы.
-
Пример:
>>> def avg_of_2(a, b): ... return a + b / 2 ... >>> avg_of_2(4, 8) # Вернет 8 вместо 6
7.1.2.3. Ошибки времени выполнения¶
-
Причина:
Любая неполадка, возникающая во время работы программы, например: целочисленное деление на ноль, ошибка при чтении файла, исчерпание доступной памяти и др.
-
Пример:
>>> a = 5 >>> b = 0 >>> a / b Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero
7.1.2.4. Недокументированное поведение¶
-
Причина:
Серьезные ошибки, которые не проявляются при нормальном ходе выполнения программы, однако весьма опасны для безопасности всей системы в случае целенаправленной атаки.
-
Пример:
Одним из наиболее известных примеров являются SQL-инъекции — внедрение в запрос произвольного SQL-кода.
-- Код на сервере txtUserId = getRequestString("UserId"); txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId; -- "Стандартный" вызов на клиенте при UserId = 105 сформирует запрос SELECT * FROM Users WHERE UserId = 105 -- Передача в качестве 'UserId' значения -- "105; DROP TABLE Suppliers" приведет к удалению таблицы 'Suppliers' SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers
Синтаксические и ошибки времени выполнения приводят к немедленному завершению (краху (англ. Crash)) выполнения программы в отличие от логических ошибок, после которых программа может продолжить работу. Пример краха программного обеспечения приведен на Видео 7.1.3.
Видео 7.1.3 — Презентация ОС Windows 98
7.1.3. Поиск ошибок и отладка программы¶
Поиск ошибок выполняется на этапе тестирования программного обеспечения, в процессе которого происходит обнаружение, локализация и устранение ошибок — или отладка. В процессе отладки происходит определение текущих значений переменных и путей выполнения программы, приведших к сбою.
Существуют две взаимодополняющие технологии отладки:
-
использование отладчиков: программ, которые включают в себя пользовательский интерфейс для пошагового выполнения программы: оператор за оператором, функция за функцией, с остановками на некоторых строках исходного кода или при достижении определенного условия (Рисунок 7.1.7);
Рисунок 7.1.7 — Пример отладки в IDE PyCharm: выполнение «заморожено» на точке останова (англ. Breakpoint), при этом IDE отображает текущие значения переменных, дополнительные окна и параметры¶
-
вывод текущего состояния программы с помощью расположенных в критических точках программы операторов вывода — на экран, принтер, громкоговоритель или в файл. Вывод отладочных сведений в файл называется журналированием (также логгированием или аудитом) (Листинг 7.1.1).
Листинг 7.1.1 — Пример отладочного вывода с использованием функции
print()
|скачать
¶import random a = random.randint(1, 100) b = random.randint(1, 100) print(a, b) # 44 97 print(a**2 + b**2) # 11345
7.1.4. Подходы к обработке ошибок¶
При написании кода необходимо предусматривать, что в определенном месте программы может возникнуть ошибка и дополнять код на случай ее возникновения.
Существует два ключевых подхода программирования реакции на возможные ошибки:
-
«Семь раз отмерь, один раз отрежь» — LBYL (англ. Look Before You Leap);
Суть подхода: прежде чем выполнить основное действие выполняются проверки — не получится ли деления на ноль, есть ли файл на диске и т.д.
-
«Легче попросить прощения, чем разрешения» — EAFP (англ. «It’s Easier To Ask Forgiveness Than Permission»).
Суть подхода: сначала выполняется основное действие, а затем, если возникнут, обрабатываются ошибки. Данный механизм поддерживается в различных языках программирования и называется обработкой исключений, а сама ошибочная ситуация — исключением.
В Листинге 7.1.2 приведен пример сравнения двух подходов.
Листинг 7.1.2 — Псевдокод функций, использующих разные подходы к обработке ошибок: «Семь раз отмерь, один раз отрежь» и «Легче попросить прощения, чем разрешения»¶
Обе функции возвращают решение линейного уравнения и НИЧЕГО, если 'a' = 0 ФУНКЦИЯ найти_корень_1(a, b): ЕСЛИ a не равно 0 ВЕРНУТЬ -b / a ИНАЧЕ ВЕРНУТЬ НИЧЕГО ФУНКЦИЯ найти_корень_2(a, b): ОПАСНЫЙ БЛОК КОДА # Внутри данного блока пишется код, который ВЕРНУТЬ -b / a # потенциально может привести к ошибкам ЕСЛИ ПРОИЗОШЛА ОШИБКА # В случае деления на 0 попадаем сюда ВЕРНУТЬ НИЧЕГО
Подход «Семь раз отмерь, один раз отрежь» имеет определенные минусы:
-
проверки могут уменьшить читаемость и ясность основного кода;
-
код проверки может дублировать значительную часть работы, осуществляемой основным кодом;
-
разработчик может легко допустить ошибку, забыв какую-либо из проверок;
-
ситуация может изменится между моментом проверки и моментом выполнения операции.
В языках программирования, поддерживающих обработку исключений, рекомендуемым подходом к обработке ошибок является EAFP.
7.1.5. Обработка исключений в Python¶
7.1.5.1. Понятия исключения¶
При возникновении ошибки времени выполнения Python создает (возбуждает) специальный объект — исключение, который позволяет однозначно характеризовать возникшую ошибочную ситуацию. Выбор подходящего исключения происходит из встроенной иерархии классов-исключений (фрагмент):
-
BaseException
(базовое исключение)-
SystemExit
(исключение, порождаемое функциейsys.exit()
при выходе из программы) -
KeyboardInterrupt
(прерывании программы пользователем,Ctrl+C
) -
Exception
(базовое несистемное исключение)-
ArithmeticError
(арифметическая ошибка)-
FloatingPointError
(неудачное выполнение операции с плавающей запятой) -
OverflowError
(результат арифметической операции слишком велик для представления) -
ZeroDivisionError
(деление на ноль)
-
-
LookupError
(некорректный индекс или ключ)-
IndexError
(индекс не входит в диапазон элементов) -
KeyError
(несуществующий ключ)
-
-
MemoryError
(недостаточно памяти) -
NameError
(не найдено переменной с таким именем) -
OSError
(ошибка, связанная с ОС — есть подклассы, напримерFileNotFoundError
) -
SyntaxError
(синтаксическая ошибка, включает классыIndentationError
иTabError
) -
SystemError
(внутренняя ошибка) -
TypeError
(операция применена к объекту несоответствующего типа) -
ValueError
(аргумент правильного типа, но некорректного значения)
-
-
Пример встроенного возбуждения исключения:
>>> "я - строка" / 5 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for /: 'str' and 'int'
7.1.5.2. Конструкция try
¶
Придерживаясь идеологии «Легче попросить прощения, чем разрешения», Python предусматривает конструкцию try
для обработки возникающих исключений.
-
try
¶ -
except
¶ -
else
¶ -
finally
¶ -
try: # (try строго 1) try_ suite # код, который может выполниться с ошибкой except exception_group1 as var1: # (except - 0 (если есть finally) и более) except_suite1 # код, выполняемый в случае исключения 'exception_group1' ... # ссылка на исключение может быть записана в 'var1' except exception_groupN as varN: except_suiteN # код, выполняемый в случае исключения 'exception_groupN' ... # except-блоков может быть произвольное кол-во else: # (else - 0 или 1) else_suite # выполняется, если try не завершен преждевременно (например, break) finally: # (finally - 0 или 1) finally_suite # код, который должен выполнится всегда (была ошибка выше или нет)
Ход выполнения:
-
код, который потенциально может привести к ошибке, помещается в блок
try
; -
в случае ошибки, код немедленно завершается и переходит в обработчик
except
(если он указан для соответствующего исключения); -
после поток выполнения переходит к
else
(если исключений не было) иfinally
(в любом случае).
На Рисунке 7.1.8 приведены общие варианты потока выполнения программы при обработке исключений.
Рисунок 7.1.8 — Варианты потока выполнения программы при обработке исключений 4¶
Обработка исключений (и соответственно идеология «Легче попросить прощения, чем разрешения») — предпочитаемый способ в Python, а использование блоков зависит от конкретной ситуации.
Наиболее общий вариант обработки исключений приведен в Листинге 7.1.3.
Листинг 7.1.3 — Наиболее простой способ обработки исключений | скачать
¶
try: x = int(input("Введите целое число x (для вычисления 1/x): ")) res = 1 / x print("1/{} = {:.2f}".format(x, res)) except: print("Произошла ошибка!") # -------------- # Примеры вывода: # Введите целое число x (для вычисления 1/x): 3 # 1/3 = 0.33 # Введите целое число x (для вычисления 1/x): qwerty # Произошла ошибка!
Подобный вариант обработки исключений не рекомендуется, т.к. блок except
будет перехватывать любое исключение, что не позволит точно определить ошибку в коде. Улучшить код можно, добавив обработку исключения по классу (Листинг 7.1.4).
Листинг 7.1.4 — Обработка общего класса исключений Exception
| скачать
¶
try: x = int(input("Введите целое число x (для вычисления 1/x): ")) res = 1 / x print("1/{} = {:.2f}".format(x, res)) except Exception as err: print("Произошла ошибка!") print("Тип:", type(err)) print("Описание:", err) # -------------- # Примеры вывода: # Введите целое число x (для вычисления 1/x): 3 # 1/3 = 0.33 # Введите целое число x (для вычисления 1/x): 5.5 # Произошла ошибка! # Тип: <class 'ValueError'> # Описание: invalid literal for int() with base 10: '5.5'
Рекомендуемым способом обработки исключений является как можно большая конкретизация класса исключения (Листинг 7.1.5).
Листинг 7.1.5 — Обработка конкретных классов исключений | скачать
¶
try: x = int(input("Введите целое число x (для вычисления 1/x): ")) res = 1 / x print("1/{} = {:.2f}".format(x, res)) except ZeroDivisionError: print("На ноль делить нельзя!") except ValueError as err: # 'err' содержит ссылку на исключение print("Будьте внимательны:", err) except (FileExistsError, FileNotFoundError): # Исключения можно перечислять в виде кортежа print("Этого никогда не случится - мы не работаем с файлами") except Exception as err: # Все, что не обработано выше и является потомком 'Exception', # будет обработано здесь print("Произошла ошибка!") print("Тип:", type(err)) print("Описание:", err) # -------------- # Примеры вывода: # Введите целое число x (для вычисления 1/x): 3 # 1/3 = 0.33 # Введите целое число x (для вычисления 1/x): 0 # На ноль делить нельзя! # Введите целое число x (для вычисления 1/x): qwerty # Будьте внимательны: invalid literal for int() with base 10: 'qwerty'
7.1.5.3. Возбуждение исключений (raise
)¶
Исключения являются не только механизмом обработки ошибок, но и удобным средством управления потоком выполнения. Так, необходимое исключение можно возбудить вручную, когда это необходимо, используя конструкцию raise
.
-
raise
¶ -
raise exception(args) # явное указание класса возбуждаемого исключения # или raise # 1) повторное возбуждение активного исключения (re-raise) # внутри блока except # 2) 'TypeError' по умолчанию
Возбуждаемое исключение может быть как встроенным (если соответствует по смыслу), так и пользовательским (создаваемым самостоятельно).
В Листинге 7.1.6 приведен пример использование оператора raise
.
Листинг 7.1.6 — Использование raise
для управления потоком выполнения | скачать
¶
MIN = 1 MAX = 10 try: x = int(input("Введите целое число от {} до {}: ".format(MIN, MAX))) if not MIN <= x <= MAX: # Возбудив исключение, его можно будет обработать в except # вместе с другими похожими исключениями raise ValueError("Число лежит вне интервала [{}; {}]!".format(MIN, MAX)) print("Спасибо!") except ValueError as err: # 'err' содержит ссылку на исключение print("Будьте внимательны:", err) # -------------- # Примеры вывода: # Введите целое число от 1 до 10: 5 # Спасибо! # Введите целое число от 1 до 10: 15 # Будьте внимательны: Число лежит вне интервала [1; 10]! # Введите целое число от 1 до 10: qwerty # Будьте внимательны: invalid literal for int() with base 10: 'qwerty'
7.1.5.4. Особенности обработки исключений внутри функций¶
Обработка исключений аналогично производится и внутри функций, однако, необходимо писать код так, чтобы вызывающий код знал о случившемся, если это влияет на его дальнейшую работу.
Разница в обработке исключений приведена в Листинге 7.1.7.
Листинг 7.1.7 — Различная обработка исключений в функции | скачать
¶
# Ниже представлены 3 варианта обработки исключений в функциях # Основное правило - обработка исключений внутри возможна и нужна, однако # вызывающий код должен также знать о случившемся, если # влияет на дальнейшую работу def get_1_x(x): """Вернуть 1/x. Функция не обрабатывает исключения - ответственность на вызывающем коде. """ return 1/x def get_2_x(x): """Вернуть 2/x. Функция обрабатывает исключения, "затушив" ошибку - вызывающий код не будет знать, сработала функция правильно или нет. Данный способ использовать не рекомендуется! """ try: return 2/x except Exception as e: print("Внутри произошла ошибка...", e) def get_3_x(x): """Вернуть 3/x. Функция не только обрабатывает исключения, но перевозбуждает его: в результате вызывающий так же получает возникшее исключение. Внутренняя обработка исключений может быть полезна, если в целом результат функции не связан с внутренней ошибкой. """ try: return 3/x except Exception as e: print("Внутри произошла ошибка...", e) raise funcs = (get_1_x, get_2_x, get_3_x) # Вызываем каждую функцию с "ошибочным" параметром for func in funcs: try: print("-" * 50) print("Запущена функция:", func.__name__) print(func(0)) except Exception as e: print("Произошла ошибка: {}.".format(e)) # ------------- # Пример вывода: # -------------------------------------------------- # Запущена функция: get_1_x # Произошла ошибка: division by zero. # -------------------------------------------------- # Запущена функция: get_2_x # Внутри произошла ошибка... division by zero # None # -------------------------------------------------- # Запущена функция: get_3_x # Внутри произошла ошибка... division by zero # Произошла ошибка: division by zero.
7.1.5.5. Утверждения (assert
)¶
Еще один способ, используемый для борьбы с ошибками — использование утверждений — специальных конструкций, выполняющих проверку произвольного условия на истинность.
В Python утверждения поддерживаются оператором assert
.
-
assert
¶ -
assert boolean_expression[, optional_expression] # boolean_expression: логическое выражение для проверки # optional_expression: необязательное сообщение (строка)
Если boolean_expression
возвращает False
, возбуждается исключение AssertionError
с сообщением optional_expression
(если задано).
Пример использования утверждений приведен в Листинге 7.1.8.
Листинг 7.1.8 — Использование утверждений в Python | скачать
¶
# Использование оператора assert # поможет отследить неверно реализованную функцию def add_to_list(x, lst=[]): # Использование assert здесь оправдано - список всегда # подразумевается пустым assert len(lst) == 0, "Список должен быть пуст!" lst.append(x) return lst print(add_to_list(1)) print(add_to_list(2)) # ------------- # Пример вывода: # [1] # Traceback (most recent call last): # File "07_01_08_a.py", line 15, in <module> # print(add_to_list(2)) # File "07_01_08_a.py", line 8, in add_to_list # assert len(lst) == 0, "Список должен быть пуст!" # AssertionError: Список должен быть пуст!
Примечание
В отличие от исключений утверждения являются отладочным инструментом и могут быть отключены при компиляции/интерпретации программы
7.1.5.6. Исключения или утверждения?¶
При выборе, использовать исключения или утверждения, придерживайтесь правил (Таблица 7.1.1).
№ |
Исключения |
Утверждения |
---|---|---|
Обработка ошибок, которые могут произойти во время выполнения программы (неправильный ввод пользователя и т.п.) |
Проверка ситуаций, которые предположительно не могут произойти |
|
1 |
Проверка параметров общедоступных функций (исключения не могут быть отключены — проверка будет осуществлена всегда, а также класс исключения говорит о конкретном типе ошибки в отличие от утверждения) |
Проверка предусловий, постусловий и инвариантов в необщедоступном коде (локальных функциях, внутреннем коде и т.д.) |
2 |
Предоставление информации об ошибке пользователю |
Предоставление информации об ошибке команде разработки |
3 |
Восстановление из ошибочной ситуации (например, дальнейшая работа программы, если не удалось открыть файл) |
Проверка невозможных ситуаций, свидетельствующих о серьезной ошибке в коде (например, выход за границы списка) |
Примеры использования исключения и утверждений приведены в Листингах 7.1.9 (а-в).
Листинг 7.1.9 (а) — Использование исключений и утверждений в Python | скачать
¶
# Исключения и утверждения могут проверять параметры функции, # выступая в т.ч. более строгим вариантом документации def fact(x): """Вернуть факториал 'x'. Не передавайте числа больше 15 во избежание переполнения памяти. """ if x <= 1: return 1 else: return x * fact(x-1) def fact_save_1(x): assert x <= 15, "Не передавайте числа больше 15 во избежание переполнения памяти" return fact(x) def fact_save_2(x): if not x <= 15: raise ValueError("Не передавайте числа больше 15 во избежание " "переполнения памяти") return fact(x) print("{:>3} {:>20} {:>20}".format(*("x", "fact()", "fact_save()"))) for x in (5, 20): print("{:3}".format(x), end=" ") print("{:20}".format(fact(x)), end=" ") # print("{:20}".format(fact_save_1(x))) print("{:20}".format(fact_save_2(x))) # ------------- # Пример вывода: # x fact() fact_save() # 5 120 120 # 20 2432902008176640000 Traceback (most recent call last): # File "07_01_08_b.py", line 23, in <module> # print("{:20}".format(fact_save(x))) # File "07_01_08_b.py", line 15, in fact_save # assert x <= 15, "Не передавайте числа больше 15 во избежание переполнения памяти" # AssertionError: Не передавайте числа больше 15 во избежание переполнения памяти # # x fact() fact_save() # 5 120 120 # 20 2432902008176640000 Traceback (most recent call last): # File "07_01_08_b.py", line 34, in <module> # print("{:20}".format(fact_save_2(x))) # File "07_01_08_b.py", line 24, in fact_save_2 # raise ValueError("Не передавайте числа больше 15 во избежание " # ValueError: Не передавайте числа больше 15 во избежание переполнения памяти
Листинг 7.1.9 (б) — Использование исключений и утверждений в Python | скачать
¶
# Использование оператора assert для проверки входных и выходных данных def make_call(accounts, account_id, mins, costs_per_min): """Списать со счета 'account' на 'value' баллов в случае звонка. Параметры: - accounts (dict): словарь со всеми счетами абонентов; - account_id (int): идентификатор абонента в словаре 'accounts'; - mins (int): количество минут разговора; - costs_per_min (int): стоимость минуты разговора. """ def get_costs(mins, costs_per_min): """Вернуть стоимость звонка. Параметры: - mins (int): количество минут разговора; - costs_per_min (int): стоимость минуты разговора. """ return -mins * costs_per_min # Проверка типов assert isinstance(mins, int), "Параметр 'mins' имеет неверный тип!" assert isinstance(costs_per_min, (int, float)), "Параметр 'costs_per_min' имеет неверный тип!" # Проверка значений assert mins > 0, "Параметр 'mins' должен быть > 0" assert costs_per_min >= 0, "Параметр 'costs' должен быть >= 0" # Расчет (стоимость звонка не должна быть меньше 0) costs_total = get_costs(mins, costs_per_min) assert costs_total >= 0, "Расчет стоимости звонка был осуществлен неверно!" accounts[account_id] -= costs_total # Словарь ID=Баланс accounts = {"Василий Иванов": 100} print(accounts) try: make_call(accounts, "Василий Иванов", mins=4, costs_per_min=2) except Exception as e: print("Во время списывания стоимости звонка произошла ошибка:", e) print(accounts)
Листинг 7.1.9 (в) — Использование исключений и утверждений в Python | скачать
¶
# Совместное использование исключений и утверждений weekday_names = { 1: "Понедельник", 2: "Вторник", 3: "Среда", 4: "Четверг", 5: "Пятница", 6: "Суббота", 7: "Воскресенье" } def weekday_name(weekday): """Вернуть название дня недели. Нумерация с 1. Параметры: weekday (int): номер дня недели. Исключения: - TypeError: 'weekday' не int; - ValueError: 'weekday' не число от 1 до 7. Результат: str: название дня недели. """ # "Невозможная" ситуация - словарь 'weekday_names' может быть # "испорчен" - проверяется с помощью assert. assert weekday_names is not None and isinstance(weekday_names, dict), "Внутренняя ошибка программы. Обратитесь в разрабочику." # Параметры функции проверяются с помощью исключений if not isinstance(weekday, int): raise TypeError("Параметр 'weekday' должен быть типа 'int'.") if weekday not in weekday_names: raise ValueError("Параметр 'weekday' должен быть целым числом " "от 1 до 7.") return weekday_names[weekday] # Блок try используется в любом случае т.к. может возникнуть ошибка # независимо от того, используется пользовательский ввод, # чтение данных из какого-либо источника или просто вызов функции while True: try: weekday = int(input("Введите номер дня недели (1-7): ")) # if not 1 <= weekday <= 7: # raise ValueError("Номер дня недели должен быть целым числом " # "от 1 до 7.") # Раскомментируйте код ниже, чтобы получить срабатывание assert # weekday_names = None print("Это -", weekday_name(weekday)) break except TypeError as err: print("Проверьте, что введено целое число.") except ValueError as err: print("Проверьте, что введено целое число, и оно " "находится в допустимых границах.") except Exception as err: print("Ошибка при определении названия дня недели.") print(err) # Запись в лог информации об ошибке
7.1.6. Рекомендации¶
Программный код должен быть написан с учетом того, что в любом его месте может возникнуть ошибка, для чего необходимо эффективно использовать соответствующие средства языка программирования:
-
код, который потенциально может привести к ошибкам, должен быть помещен в блок
try
; -
блок
except
должен:-
обрабатывать исключения максимально конкретно (указывать конкретные классы); стоит определять свои классы исключений, когда это это имеет смысл;
-
категорически не следует «тушить» исключения (писать пустой или бессмысленный
except
); -
в блоках
except
следует снова возбуждать исключения (raise
), которые не обрабатываются явно, передавая обработку в участок кода, который должен определять дальнейшие действия программы;
-
-
блок
finally
следует использовать для освобождения ресурсов (это может быть закрытие файла или сетевого соединения), независимо от того, прошла операция успешно или нет.
- 1
-
Sebesta, W.S Concepts of Programming languages. 10E; ISBN 978-0133943023.
- 2
-
Python — официальный сайт. URL: https://www.python.org/.
- 3
-
Python — FAQ. URL: https://docs.python.org/3/faq/programming.html.
- 4
-
Саммерфилд М. Программирование на Python 3. Подробное руководство. — М.: Символ-Плюс, 2009. — 608 с.: ISBN: 978-5-93286-161-5.
- 5
-
Лучано Рамальо. Python. К вершинам мастерства. — М.: ДМК Пресс , 2016. — 768 с.: ISBN: 978-5-97060-384-0, 978-1-491-94600-8.
- 6
-
5 худших багов в истории. URL: https://tproger.ru/articles/5-worst-bugs-in-history/.
- 7
-
List of software bugs. URL: https://en.wikipedia.org/wiki/List_of_software_bugs.
- 8
-
Toyota: 81 514 нарушений в коде. URL: https://habrahabr.ru/company/pvs-studio/blog/310862/.
- 9
-
Mariner-1. URL: https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%80%D0%B8%D0%BD%D0%B5%D1%80-1#/media/File:Atlas_Agena_with_Mariner_1.jpg.
- 10
-
Therac-25. URL: https://upload.wikimedia.org/wikipedia/commons/b/bd/Clinac_2rtg.jpg.
- 11
-
ЗРК Patriot. URL: http://vpk.name/file/img/Patriot_antimissile.t.jpg.
- 12
-
Incorrectly Calculated Range Gate. URL: https://hsto.org/files/853/04a/1dc/85304a1dc1b6499d94d5394436e42faf.jpg.
- 13
-
Табло показывает 3 января 1900 года, вместо 3 января 2000 года. Франция. URL: https://upload.wikimedia.org/wikipedia/commons/f/fb/Bug_de_l%27an_2000.jpg.
- 14
-
MY 2007–2010 Lexus ES 350. URL: http://pictures.topspeed.com/IMG/crop/201108/lexus-es-350-6_1600x0w.jpg.
Понятие исключений и ошибок
Любой, даже самый опытный программист, в ходе разработки программ допускает различного рода ошибки, приводящие к тому, что программа либо не работает вообще,
либо работает, но выполняет вовсе не то, что задумывалось. Причинами ошибок могут быть как неправильное использование синтаксиса и пунктуации языка
(синтаксические ошибки), так и неверное понимание логики программы (семантические ошибки, которые в Python принято называть
исключениями). При этом, если первый тип ошибок легко обнаружить с помощью интерпретатора, то на устранение логических ошибок
порой может потребоваться не один час, поскольку такие ошибки обнаруживаются уже непосредственно в ходе использования программы, проявляясь либо в виде сбоев
в ее работе, либо в получении совершенно непредвиденных результатов (см. пример №1).
# Синтаксические ошибки обнаружить легко.
# Имя переменной начали с цифры.
# SyntaxError: invalid decimal literal
# 35_days = 35
# Строка должна содержать запись целого числа.
# ValueError: invalid literal for int() with base 10: 'три'
# num = int('три')
# Числа и строки складывать нельзя.
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
# res = 5 + 'один'
# Логические ошибки всплывут потом.
# На первый взгляд все верно, и программа даже будет
# работать, но только до тех пор, пока пользователь
# не введет ноль или неприводимое к числу значение.
a = int(input('Введите число: '))
print(10/a)
Введите число: 0
ZeroDivisionError: division by zero
Пример №1. Синтаксические и логические ошибки в Python.
В примере мы показали лишь несколько видов исключений. На самом деле их намного больше. И очень важно знать о них, а также иметь инструменты для их обнаружения и
обработки. Именно поэтому в стандартной библиотеке Python присутствует внушительный набор готовых классов, представляющих различные
виды исключений и синтаксических ошибок. Все они перечислены в разделе Built-in exceptions,
где также представлена и подробная иерархия имеющихся классов исключений. Что касается обработки ошибок, то для этого Python
предоставляет ряд специальных инструкций, которые мы и будем рассматривать в данном параграфе.
Инструкция try/except/else/finally
Пожалуй, основной «рабочей лошадкой» по обработке исключений в Python является составная инструкция
try/except/else/finally, которая имеет следующий общий формат:
# Сначала выполняется основной блок инструкций.
try:
<Основные инструкции>
# Запускается, если в блоке try возникло исключение Exception_1.
except Exception_1:
<Инструкции обработки исключения>
# Запускается, если возникло любое из перечисленных исключений.
except (Exception_2, Exception_3):
<Инструкции обработки исключений>
# Работаем с экземпляром Exception_4, как с err.
except Exception_4 as err:
<Инструкции обработки исключения>
# Запускается для всех видов исключений.
except:
<Инструкции обработки исключений>
# Запускается, если в блоке try не возникло исключений.
else:
<Дополнительные инструкции>
# Запускается в любом случае.
finally:
<Финальные инструкции>
Первыми выполняются инструкции основного блока try. При обнаружении в нем ошибок, интерпретатор пытается найти соответствующий
возникшему исключению блок except и, в случае наличия такового, выполняет его инструкции. После того как исключение будет
обработано, оно уничтожается, а программа пытается продолжить работу в штатном режиме. Если же среди имеющихся блоков except
соответствия найдено не будет, исключение переадресуется инструкции try, стоящей
выше в программе, или на верхний уровень процесса, что скорее всего вынудит интерпретатор аварийно завершить работу программы и вывести сообщение об ошибке по
умолчанию. В случае отсутствия ошибок в блоке try интерпретатор пропускает все блоки except и
начинает выполнять инструкции необязательного блока else, который разрешается использовать только при наличии хотя бы одного блока
except. При этом стоит помнить, что при наличии ошибок в блоке try инструкции данного блока
выполняться не будут. Что касается необязательного блока finally, то он используется для каких-либо завершающих операций, например,
закрытия файлов или открытых соединений с сервером. Его инструкции выполняются всегда, вне зависимости от того, возникли исключения в каком-либо блоке, включая блок
else, или нет (см. пример №2).
try:
# Здесь исключения могут возбудиться из-за
# ввода нечислового значения или нуля.
a = int(input('Введите число: '))
print('10/{} = {}'.format(a, 10/a))
# Если введено не целое число.
except ValueError:
print('Введите целое число!')
# Если введен ноль.
except ZeroDivisionError:
print('На ноль делить нельзя!')
# Выполнится только при отсутствии ошибок.
else:
print('Операция прошла успешно!')
# Выполняется в любом случае.
finally:
print('Для завершения нажмите «Enter»!')
# Чтобы окно консоли не закрылось.
input()
Введите число: 0
На ноль делить нельзя!
Для завершения нажмите «Enter»!
-------------------------------
Введите число: один
Введите целое число!
Для завершения нажмите «Enter»!
-------------------------------
Введите число: 20
10/20 = 0.5
Операция прошла успешно!
Для завершения нажмите «Enter»!
Пример №2. Инструкция try/except/else/finally в Python (часть 1).
Стоит заметить, что в отличие от блоков except блок finally не останавливает распространение
исключений до вышестоящей инструкции try или до обработчика исключений по умолчанию, т.е. в случае возникновения ошибок
инструкции, следующие за блоком finally в программе, выполняться не будут (см. пример №3).
# Внешняя инструкция try.
try:
# Внутреняя инструкция try.
try:
# Здесь исключения могут появиться из-за
# ввода нечислового значения или нуля.
a = int(input('Введите число: '))
print('10/{} = {}'.format(a, 10/a))
# Выполняется в любом случае.
finally:
print('Внутренний блок finally.')
# Выполняется при отсутствии ошибок
# во внутреннем блоке try.
print('Все ОК! Ошибок нет!')
# Если введено не целое число или ноль.
except (ValueError, ZeroDivisionError):
print('Неверный ввод!')
# Выполнится только при отсутствии ошибок.
else:
print('Операция прошла успешно!')
# Выполняется в любом случае.
finally:
print('Внешний блок finally.')
# Чтобы окно консоли не закрылось.
input()
Введите число: 7
10/7 = 1.4285714285714286
Внутренний блок finally.
Все ОК! Ошибок нет!
Операция прошла успешно!
Внешний блок finally.
------------------------
Введите число: 0
Внутренний блок finally.
Неверный ввод!
Внешний блок finally.
Пример №3. Инструкция try/except/else/finally в Python (часть 2).
Если необходимо перехватывать сразу все возможные исключения, разрешается использовать инструкцию except без указания классов исключений.
Эта особенность может быть весьма удобна при разработке своих собственных обработчиков ошибок. Однако при использовании такого варианта могут перехватываться нежелательные
системные исключения, не связанные с работой создаваемого программного кода, а также случайно может прерываться распространение исключений, предназначенных для других
обработчиков. Данная проблема была частично решена в Python 3.0 за счет введения альтернативы в виде суперкласса
Exception, представляющего все прикладные исключения, но игнорирующего исключения, связанные с завершением программы (см. пример
№4).
# Основной блок try.
try:
# Здесь исключения могут возбудиться из-за
# ввода нечислового значения или нуля.
a = int(input('Введите число: '))
print('10/{} = {}'.format(a, 10/a))
# Выводим строковое представление исключения.
except Exception as err:
print(err)
# Выполнится только при отсутствии ошибок.
else:
print('Операция прошла успешно!')
# Выполняется в любом случае.
finally:
# Чтобы окно консоли не закрылось.
input()
Введите число: one
invalid literal for int() with base 10: 'one'
------------------------
Введите число: 0
division by zero
------------------------
Введите число: 10
10/10 = 1.0
Операция прошла успешно!
Пример №4. Инструкция try/except/else/finally в Python (часть 3).
В конце добавим, что помимо основного варианта использования try/except/else/finally инструкция
try может быть использована и в варианте try/finally, т.е. без блоков обработки исключений
except. Как не трудно догадаться, основной задачей такого варианта использования инструкции является выполнение заключительных
операций после выполнения кода основного блока.
Инструкция raise
В примерах выше мы полагались на то, что интерпретатор автоматически определит появление ошибок во время выполнения программного кода. Однако в
Python присутствует возможность и явного возбуждения исключений с помощью инструкции
raise. Все что от нас требуется, это указать через пробел имя класса или экземпляра
возбуждаемого исключения. Также разрешается использовать пустую инструкцию, тогда будет повторно возбуждено самое последнее исключение
(см. пример №5).
try:
# Возбудим исключение IndexError принудительно.
# Экземпляр исключения создается автоматически.
raise IndexError
# Обработаем его.
except IndexError:
print('Перехватили IndexError!')
try:
# Все тоже самое, но здесь создаем экземпляр сами.
raise IndexError()
# Обработаем его.
except IndexError:
print('Перехватили IndexError!')
# Вложенная инструкция try.
try:
# Возбудили последнее исключение еще раз.
raise
# Обработаем его.
except IndexError:
print('Перехватили IndexError!')
Перехватили IndexError!
Перехватили IndexError!
Перехватили IndexError!
Пример №5. Инструкция raise в Python (часть 1).
Инструкция raise может использоваться для явного возбуждения не только встроенных исключений, но и созданных пользователем.
Пользовательские исключения представляют собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса
Exception (см. пример №6).
# Создаем свой класс исключений, который
# наследуется от встроенного Exception.
class OnlyAlpha(Exception):
# Переопределим строковое представление.
def __str__(self):
return 'Разрешены только буквы!'
# Определим функцию проверки строки на
# наличие сторонних небуквенных символов.
def check_for_alpha(s):
# Если присутствуют не только буквы.
if not s.isalpha():
# Возбуждаем экземпляр своего исключения.
raise OnlyAlpha()
# Проверим, как все работает.
try:
# Просим ввести строку.
s = input('Введите имя: ')
# Проверяем ввод.
check_for_alpha(s)
# Обрабатываем польз. исключение.
except OnlyAlpha as err:
# Строковое представление экз.
print(err)
print('Попробуйте еще раз!')
# Если все прошло гладко.
else:
# Выводим введенное имя.
print('Привет, {}!'.format(s))
# В любом случае дополняем сообщением.
finally:
print('Имя – это ваш ник и логин!')
Введите имя: okpython
Привет, okpython!
Имя – это ваш ник и логин!
--------------------------
Введите имя: okpython.net
Разрешены только буквы!
Попробуйте еще раз!
Имя – это ваш ник и логин!
Пример №6. Инструкция raise в Python (часть 2).
Стоит добавить, что инструкция raise может использоваться и в формате raise/from, который используется
значительно реже, поэтому здесь мы его рассматривать не будем. Однако не поленитесь и самостоятельно посетите подраздел
«The raise statement» раздела «Simple statements» официального
справочника языка, где хотя бы бегло ознакомьтесь и с этим вариантом инструкции.
Инструкция assert
Инструкция assert представляет собой компактный условный вариант инструкции raise,
предназначенный в основном для возбуждения исключений на этапе отладки программы. В общем виде ее можно представить в формате
assert condition[, message], где condition – это условие, при невыполнении которого
(возвращается False) будет возбуждено встроенное исключение AssertionError, а также при необходимости
выведено необязательное сообщение message (см. пример №7).
# Допустим мы пишем функцию для расчета среднего
# значения чисел переданного ей списка.
def avg(li):
len_li = len(li)
# Список не должен быть пустым.
assert len_li != 0, 'Список пуст!'
# Возвращаем среднее значение.
return round(sum(li)/len_li, 3)
try:
# Запускаем функцию.
res = avg([])
# Отлавливаем исключение для обработки.
except AssertionError as my_err:
print(my_err)
# Результат выводим, если все в порядке.
else:
print('Среднее значение: {}'.format(res))
Список пуст!
Пример №7. Инструкция assert в Python.
Важно не забывать, что инструкция assert главным образом предназначена для проверки соблюдения ограничений, накладываемых самим
программистом, а не для перехвата настоящих ошибок, которые интерпретатор Python в состоянии обработать самостоятельно во время
выполнения программы. Поэтому как правило инструкцию assert не используют для выявления таких проблем, как выход индекса за
допустимые пределы, несоответствие типов или деление на ноль.
Краткие итоги параграфа
-
Основной инструкцией для обработки исключений в Python является составная инструкция try/except/else/finally.
Если в инструкциях обязательного блока try возникают ошибки, интерпретатор пытается перехватить и обработать их с помощью блоков
except. В случае отсутствия соответствий исключение передается инструкции try, стоящей выше в программе,
или на верхний уровень процесса. Необязательный блок else выполняется только при отсутствии ошибок, а необязательный блок
finally выполняется всегда. При этом в отличие от блоков except блок
finally не останавливает распространение исключений до вышестоящей инструкции try или до обработчика
исключений по умолчанию, поэтому в случае возникновения ошибок инструкции, следующие за блоком finally в программе, выполняться не будут. -
В стандартной библиотеке Python присутствует внушительный набор готовых классов, представляющих различные виды исключений и синтаксических
ошибок. Все они перечислены в разделе Built-in exceptions, где также представлена и подробная
иерархия имеющихся классов исключений. -
Важно помнить, что при использовании блока except без указания класса перехватываемого исключения интерпретатор будет пытаться перехватить
все имеющиеся виды встроенных исключений. Однако нужно быть осторожным при использовании такого варианта инструкции воизбежание перехвата нежелательных системных исключений,
не связанных с работой создаваемого программного кода. -
Для явного возбуждения исключений в Python предназначена инструкция raise, которой необходимо через
пробел передавать имя класса или экземпляра возбуждаемого исключения. Данная инструкция может использоваться для явного возбуждения не только встроенных исключений, но
и созданных пользователем. Пользовательские исключения представляют собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса
Exception. -
Для возбуждения исключений на этапе отладки программы предназначена инструкция assert condition[, message], где
condition – это условие, при невыполнении которого (возвращается False) будет возбуждено
встроенное исключение AssertionError, а также при необходимости выведено необязательное сообщение message.
Использовать инструкцию следует главным образом для проверки соблюдения ограничений, накладываемых самим программистом, а не для перехвата настоящих ошибок, которые могут
быть спокойно перехвачены интерпретатором Python.
Помогите проекту, подпишитесь!
Подписка на учебные материалы сайта оформляется сроком на один год и стоит около 15 у.е. После
подписки вам станут доступны следующие возможности.
- Доступ ко всем ответам на вопросы и решениям задач.
- Возможность загрузки учебных кодов и программ на свой компьютер.
- Доступ ко всем тренажерам и видеоурокам (когда появятся).
-
Возможность внести свой скромный вклад в развитие проекта и мира во всем мире,
а также выразить свою благодарить автору за его труд. Нам очень нужна ваша поддержка!
На страницу подписки
Вопросы и задания для самоконтроля
1. В каком случае выполняется код блока else составной инструкции
try/except/else/finally?
Показать решение.
Ответ. Инструкции необязательного блока else выполняются только при отсутствии
исключений в коде основного блока try.
2. Что произойдет с программой в случае возбуждения исключения, если в ней не предусмотреть его обработку?
Показать решение.
Ответ. В таком случае исключение будет передано обработчику предоставляемому интерпретатором по умолчанию.
Этот обработчик выведет сообщение об ошибке и завершит программу.
3. Перечислите верные форматы использования инструкции try:
try/except/else, try/else, try/finally,
try/except/finally, try/else/finally.
Показать решение.
Ответ. Блок else может использоваться только при наличии хотя бы одного блока
except, следовательно варианты try/else и try/else/finally
недопустимы.
4. В каком случае выполняется код блока finally?
Показать решение.
Ответ. Необязательный блок finally выполняется всегда. При этом в отличие от
блоков except блок finally не останавливает распространение исключений до вышестоящей
инструкции try или до обработчика исключений по умолчанию, поэтому в случае возникновения ошибок инструкции, следующие за
блоком finally в программе, выполняться не будут.
5. Присутствуют ли в коде условия ошибки? Проверьте свой ответ, запустив код на исполнение.
Показать решение.
try: a = 5; b = 0 n = a/b exept Exeption as err: print('Ошибка: «{}».'.format(err)) else: print('a/b = {}'.format(n)) finally: print('Обработка завершена!')
try:
a = 5; b = 0
n = a/b
# Правильно писать except и Exception.
except Exception as err:
print('Ошибка: «{}».'.format(err))
else:
print('a/b = {}'.format(n))
finally:
print('Обработка завершена!')
Ошибка: «division by zero».
Обработка завершена!
6. Имеется ли в Python возможность создавать и возбуждать исключения вручную?
Показать решение.
Ответ. Да, имеется. Для явного возбуждения исключений в Python предназначена
инструкция raise, которой необходимо через пробел передавать имя класса или экземпляра возбуждаемого исключения. Данная
инструкция может использоваться для явного возбуждения не только встроенных исключений, но и созданных пользователем. Пользовательские исключения представляют
собой самые обычные классы, наследуемые от классов встроенных исключений, чаще всего от класса Exception.
7. Для чего служит инструкция assert?
Показать решение.
Ответ. Для возбуждения исключений на этапе отладки программы предназначена инструкция
assert condition[, message], где condition – это условие, при невыполнении
которого (возвращается False) будет возбуждено встроенное исключение AssertionError,
а также при необходимости выведено необязательное сообщение message. Использовать инструкцию следует главным образом
для проверки соблюдения ограничений, накладываемых самим программистом, а не для перехвата настоящих ошибок, которые могут быть спокойно перехвачены
интерпретатором Python.
8. Дополнительные упражнения и задачи по теме расположены в разделе
«Обработка исключений»
нашего сборника задач и упражнений по языку программирования Python.
Быстрый переход к другим страницам
В этом руководстве вы узнаете о различных типах ошибок и исключений, встроенных в Python. Они возникают всякий раз, когда интерпретатор обнаруживает ошибки.
Мы можем совершать определенные ошибки при написании программы, которые приводят к ошибкам при попытке ее запустить. Программа завершает работу, как только обнаруживает необработанную ошибку. Эти ошибки можно в общих чертах разделить на два класса:
- Синтаксические.
- Логические (исключения).
Ошибки синтаксиса
Ошибка, вызванная несоблюдением правильной структуры (синтаксиса) языка, называется синтаксической ошибкой или ошибкой синтаксического анализа.
Давайте посмотрим на один пример:
>>> if a < 3 File "<interactive input>", line 1 if a < 3 ^ SyntaxError: invalid syntax
Как показано в примере, стрелка указывает, где синтаксический анализатор обнаружил синтаксическую ошибку.
Мы можем заметить здесь, что в операторе if отсутствует двоеточие.
Логические ошибки (исключения)
Ошибки, возникающие во время выполнения (после прохождения проверки синтаксиса), называются исключениями или логическими ошибками.
Например, они возникают, когда мы пытаемся открыть файл (для чтения), который не существует (FileNotFoundError), пытаемся разделить число на ноль (ZeroDivisionError) или импортировать несуществующий модуль (ImportError).
Каждый раз, когда возникают эти типы ошибок времени выполнения, Python создает объект исключения. Если не обработать должным образом, он распечатывает трассировку этой ошибки вместе с некоторыми подробностями о том, почему эта ошибка произошла.
Давайте посмотрим, как Python обрабатывает эти ошибки:
>>> 1 / 0 Traceback (most recent call last): File "<string>", line 301, in runcode File "<interactive input>", line 1, in <module> ZeroDivisionError: division by zero >>> open("imaginary.txt") Traceback (most recent call last): File "<string>", line 301, in runcode File "<interactive input>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: 'imaginary.txt'
Встроенные исключения
Незаконные операции могут вызывать исключения. В Python есть множество встроенных исключений, которые возникают при возникновении соответствующих ошибок. Мы можем просмотреть все встроенные исключения, используя встроенную функцию local() следующим образом:
print(dir(locals()['__builtins__']))
locals() [‘__ builtins__’] вернет модуль встроенных исключений, функций и атрибутов. dir позволяет нам перечислять эти атрибуты в виде строк.
Ниже перечислены некоторые из распространенных встроенных исключений в программировании на Python, а также ошибки, вызывающие их:
Исключение | Причина ошибки |
---|---|
AssertionError | Возникает при сбое утверждения утверждения. |
AttributeError | Возникает при сбое присвоения атрибута или ссылки. |
EOFError | Возникает, когда функция input() достигает условия конца файла. |
FloatingPointError | Возникает при сбое операции с плавающей запятой. |
ГенераторВыход | Поднимается при вызове метода генератора close(). |
ImportError | Возникает, когда импортированный модуль не найден. |
IndexError | Возникает, когда индекс последовательности выходит за пределы допустимого диапазона. |
KeyError | Возникает, когда ключ не найден в словаре. |
KeyboardInterrupt | Возникает, когда пользователь нажимает клавишу прерывания (Ctrl + C или Delete). |
MemoryError | Возникает, когда для операции не хватает памяти. |
NameError | Возникает, когда переменная не найдена в локальной или глобальной области. |
NotImplementedError | Поднят абстрактными методами. |
OSError | Возникает, когда работа системы вызывает ошибку, связанную с системой. |
OverflowError | Возникает, когда результат арифметической операции слишком велик для представления. |
ReferenceError | Возникает, когда слабый ссылочный прокси используется для доступа к референту со сборкой мусора. |
Ошибка выполнения | Возникает, когда ошибка не попадает ни в одну другую категорию. |
StopIteration | Вызывается функцией next(), чтобы указать, что итератор больше не возвращает элемент. |
Синтаксическая ошибка | Возникает парсером при обнаружении синтаксической ошибки. |
IndentationError | Возникает при неправильном отступе. |
TabError | Возникает, когда отступ состоит из несовместимых знаков табуляции и пробелов. |
Системная ошибка | Возникает, когда интерпретатор обнаруживает внутреннюю ошибку. |
SystemExit | Вызывается функцией sys.exit(). |
TypeError | Возникает, когда функция или операция применяется к объекту неправильного типа. |
UnboundLocalError | Возникает, когда делается ссылка на локальную переменную в функции или методе, но с этой переменной не привязано никакого значения. |
UnicodeError | Возникает при возникновении ошибки кодирования или декодирования, связанной с Unicode. |
UnicodeEncodeError | Возникает, когда во время кодирования возникает ошибка, связанная с Unicode. |
UnicodeDecodeError | Возникает, когда во время декодирования возникает ошибка, связанная с Unicode. |
UnicodeTranslateError | Возникает, когда во время перевода возникает ошибка, связанная с Unicode. |
ValueError | Возникает, когда функция получает аргумент правильного типа, но неправильное значение. |
ZeroDivisionError | Возникает, когда второй операнд деления или операции по модулю равен нулю. |
При необходимости мы также можем определить собственные исключения.
Встроенные и определяемые пользователем исключения в Python можно обрабатывать с помощью операторов try, except и finally.
47422cookie-checkОшибки и встроенные исключения в Python