При возникновении ошибки времени выполнения 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'
Конструкция try
Придерживаясь идеологии «Легче попросить прощения, чем разрешения», Python предусматривает конструкцию try для обработки возникающих исключений.
tryexceptelsefinally
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 (в любом случае).
Наиболее простой способ обработки исключений
try: x =int(input("Введите целое число x (для вычисления 1/x): ")) res =1/ xprint("1/{} = {:.2f}".format(x, res))except:print("Произошла ошибка!")# --------------# Примеры вывода:# Введите целое число x (для вычисления 1/x): 3# 1/3 = 0.33# Введите целое число x (для вычисления 1/x): qwerty# Произошла ошибка!
Подобный вариант обработки исключений не рекомендуется, т.к. блок except будет перехватывать любое исключение, что не позволит точно определить ошибку в коде. Улучшить код можно, добавив обработку исключения по классу (Листинг 7.1.4).
Обработка общего класса исключенийException
try: x =int(input("Введите целое число x (для вычисления 1/x): ")) res =1/ xprint("1/{} = {:.2f}".format(x, res))exceptExceptionas 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'
Рекомендуемым способом обработки исключений является как можно большая конкретизация класса исключения.
Обработка конкретных классов исключений
try: x =int(input("Введите целое число x (для вычисления 1/x): ")) res =1/ xprint("1/{} = {:.2f}".format(x, res))exceptZeroDivisionError:print("На ноль делить нельзя!")exceptValueErroras err:# 'err' содержит ссылку на исключениеprint("Будьте внимательны:", err)except (FileExistsError,FileNotFoundError):# Исключения можно перечислять в виде кортежаprint("Этого никогда не случится - мы не работаем с файлами")exceptExceptionas 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'
Возбуждение исключений (raise)
Исключения являются не только механизмом обработки ошибок, но и удобным средством управления потоком выполнения. Так, необходимое исключение можно возбудить вручную, когда это необходимо, используя конструкцию raise.
raise
raiseexception(args)# явное указание класса возбуждаемого исключения# илиraise# 1) повторное возбуждение активного исключения (re-raise)# внутри блока except# 2) 'TypeError' по умолчанию
Возбуждаемое исключение может быть как встроенным (если соответствует по смыслу), так и пользовательским (создаваемым самостоятельно).
В Листинге 7.1.6 приведен пример использование оператора raise.
Листинг 7.1.6 - Использование raise для управления потоком выполнения | скачать
MIN =1MAX =10try: x =int(input("Введите целое число от {} до {}: ".format(MIN, MAX)))ifnot MIN <= x <= MAX:# Возбудив исключение, его можно будет обработать в except# вместе с другими похожими исключениямиraiseValueError("Число лежит вне интервала [{}; {}]!".format(MIN, MAX))print("Спасибо!")exceptValueErroras 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.7.
Листинг 7.1.7 - Различная обработка исключений в функции | скачать
# Ниже представлены 3 варианта обработки исключений в функциях# Основное правило - обработка исключений внутри возможна и нужна, однако# вызывающий код должен также знать о случившемся, если# влияет на дальнейшую работуdefget_1_x(x):"""Вернуть 1/x. Функция не обрабатывает исключения - ответственность на вызывающем коде. """return1/xdefget_2_x(x):"""Вернуть 2/x. Функция обрабатывает исключения, "затушив" ошибку - вызывающий код не будет знать, сработала функция правильно или нет. Данный способ использовать не рекомендуется! """try:return2/xexceptExceptionas e:print("Внутри произошла ошибка...", e)defget_3_x(x):"""Вернуть 3/x. Функция не только обрабатывает исключения, но перевозбуждает его: в результате вызывающий так же получает возникшее исключение. Внутренняя обработка исключений может быть полезна, если в целом результат функции не связан с внутренней ошибкой. """try:return3/xexceptExceptionas e:print("Внутри произошла ошибка...", e)raisefuncs = (get_1_x, get_2_x, get_3_x)# Вызываем каждую функцию с "ошибочным" параметромfor func in funcs:try:print("-"*50)print("Запущена функция:", func.__name__)print(func(0))exceptExceptionas 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.