Обработка/перехват исключений try/except в Python
Можно писать программы, которые обрабатывают выбранные исключения. Посмотрите на следующий пример, который запрашивает ввод у пользователя до тех пор, пока не будет введено правильное целое число, но позволяет пользователю прерывать программу, используя Ctrl-C или что-либо поддерживаемое операционной системой. Обратите внимание, что сгенерированное пользователем прерывание сигнализируется возбуждением исключения KeyboardInterrupt .
while True: try: x = int(input("Пожалуйста, введите целое число: ")) break except ValueError: print("Это не целое число. Попробуйте снова. ")
Оператор try/except работает следующим образом:
- Сначала выполняется инструкция try — код между ключевыми словами try и except .
- Если исключение не возникает, инструкция except пропускается и выполнение оператора try завершается.
- Если во время выполнения кода в инструкции try возникает исключение, остальная часть кода этого блока пропускается. Затем, если тип исключения соответствует исключению, записанному после ключевого слова except , блок кода этой инструкции except выполняется, а затем выполнение программы продолжается после всей конструкции try .
- Если возникает исключение, которое не соответствует исключению, записанному в инструкции except , оно передается внешним операторам try . Если обработчик не найден, то это считается необработанным исключением, следовательно выполнение останавливается с сообщением об ошибке выполнения.
Оператор try может содержать несколько инструкций except , чтобы указать обработчики для различных исключений. В этом случае будет выполнен только один обработчик. Обработчики обрабатывают исключения, возникающие только в соответствующей конструкции try , а не в других обработчиках, например вложенных, того же оператора try . Инструкция except может иметь несколько исключений в виде кортежа, заключенного в скобки, например:
except (RuntimeError, TypeError, NameError): pass
Класс в инструкции except совместим с исключением, если это тот же самый класс или его базовый класс, но не наоборот. Инструкция except , перечисляющая производный класс, не совместима с базовым классом. Например, следующий код будет печатать B , C , D в таком порядке:
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print('D') except C: print('C') except B: print('B')
Обратите внимание, что если бы инструкции except были отменены (с первым исключением B), то вывелось бы B , B , B — срабатывает первое совпадающее предложение except .
Последняя инструкция except может опустить имя исключения. Используйте это с крайней осторожностью, таким образом можно легко замаскировать реальную ошибку программирования! Здесь можно использовать sys.exc_info() или модуль traceback для вывода сообщения об ошибке или ее сохранения для дельнейшего анализа, а затем повторно вызвать исключение при помощи оператора raise , что позволяет вызывающей стороне также обработать исключение:
import traceback try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: ".format(err)) except ValueError: print("Не удалось преобразовать данные в целое число.") except: print("Непредвиденная ошибка. ") # сохраняем исключение для дальнейшего анализа. with open('trace.txt', 'a') as fp: traceback.print_exc(file=fp) # повторный вызов исключения, если это необходимо. raise
В примере выше, сведения об ошибке сохраняются в файл trace.txt с использованием встроенной функции open() . Лучшей практикой сохранения исключений для дальнейшего анализа в подобных ситуациях является применение модуля logging .
Конструкция try/except может содержать необязательную инструкцию else , которая при наличии должна следовать за всеми инструкциями except . Блок кода в инструкции else будет выполнен в том случае, если код в инструкции try не вызывает исключения. Например:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('не удается открыть', arg) else: print(arg, 'имеет', len(f.readlines()), 'строк') f.close()
Использование инструкции else лучше, чем добавление дополнительного кода в инструкцию try , поскольку позволяет избежать случайного перехвата исключения, которое не было вызвано кодом, защищенным конструкцией try/except .
Когда возникает исключение, оно может иметь связанное значение, также известное как аргумент исключения. Наличие и тип аргумента зависят от типа исключения.
Инструкция except может указывать переменную после имени исключения. Переменная привязывается к экземпляру исключения с аргументами, хранящимися в экземпляре instance.args . Для удобства, экземпляр исключения определяет __str__() , так что аргументы могут быть напечатаны непосредственно без необходимости ссылаться instance.args . Кроме того, можно создать экземпляр исключения прежде, чем вызвать его и добавить любые атрибуты к нему по желанию.
try: raise Exception('spam', 'eggs') except Exception as inst: # экземпляр исключения print(type(inst)) # аргументы, хранящиеся внутри print(inst.args) # __str__ позволяет печатать args напрямую, но может # быть переопределен в подклассах исключений print(inst) # распаковка аргументов x, y = inst.args print('x =', x) print('y =', y) # # ('spam', 'eggs') # ('spam', 'eggs') # x = spam # y = eggs
Если исключение имеет аргументы, они печатаются как последняя часть («деталь») сообщения для необработанных исключений.
Обработчики исключений обрабатывают не только исключения возникающие непосредственно в предложении try , но также если они возникают внутри функций, которые вызываются, даже косвенно, в коде оператора try . Например:
def this_fails(): x = 1/0 try: this_fails() except ZeroDivisionError as err: print('Handling run-time error:', err) #Handling run-time error: division by zero
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Обработка/перехват исключений try/except
- Инструкция finally, очистка внешних ресурсов
- Тонкости работы конструкции try/except/else/finally
- Создание пользовательского класса исключения
- Обработка группы исключений, оператор except*
- Эффективная обработка исключений
- Инструкция raise, принудительный вызов исключений
- Отладочные утверждение assert
- Улучшения сообщений об ошибках 3.10
- Улучшения сообщений об ошибках 3.11
- Улучшения сообщений об ошибках 3.12
Как вызвать случайное исключение python
Иногда возникает необходимость вручную сгенерировать то или иное исключение. Для этого применяется оператор raise . Например, сгенерируем исключение
try: number1 = int(input("Введите первое число: ")) number2 = int(input("Введите второе число: ")) if number2 == 0: raise Exception("Второе число не должно быть равно 0") print("Результат деления двух чисел:", number1/number2) except ValueError: print("Введены некорректные данные") except Exception as e: print(e) print("Завершение программы")
Оператору raise передается объект BaseException — в данном случае объект Exception. В конструктор этого типа можно ему передать сообщение, которое затем можно вывести пользователю. В итоге, если number2 будет равно 0, то сработает оператор raise, который сгенерирует исключение. В итоге управление программой перейдет к блоку except , который обрабатывает исключения типа Exception:
Введите первое число: 1 Введите второе число: 0 Второе число не должно быть равно 0 Завершение программы
Создание своих типов исключений
В языке Python мы не ограничены только встроенными типами исключений и можем, применяя наследование, при необходимости создавать свои типы исключений. Например, возьмем следующий класс Person:
class Person: def __init__(self, name, age): self.__name = name # устанавливаем имя self.__age = age # устанавливаем возраст def display_info(self): print(f"Имя: Возраст: ")
Здесь класс Person в конструкторе получает значения для имени и возраста и присваивает их приватным переменным name и age. Однако при создании объекта Person мы можем передать в конструктор некорректное с точки зрения логики значение — например, отрицательное число. Одним из способов решения данной ситуации представляет генерация исключения при передаче некорректных значений.
Итак, определим следующий код программы:
class PersonAgeException(Exception): def __init__(self, age, minage, maxage): self.age = age self.minage = minage self.maxage = maxage def __str__(self): return f"Недопустимое значение: . " \ f"Возраст должен быть в диапазоне от до " class Person: def __init__(self, name, age): self.__name = name # устанавливаем имя minage, maxage = 1, 110 if minage < age < maxage: # устанавливаем возраст, если передано корректное значение self.__age = age else: # иначе генерируем исключение raise PersonAgeException(age, minage, maxage) def display_info(self): print(f"Имя: Возраст: ") try: tom = Person("Tom", 37) tom.display_info() # Имя: Tom Возраст: 37 bob = Person("Bob", -23) bob.display_info() except PersonAgeException as e: print(e) # Недопустимое значение: -23. Возраст должен быть в диапазоне от 1 до 110
В начале здесь определен класс PersonAgeException, который наследуется от класса Exception. Как правило, собственные классы исключений наследуются от класса Exception. Класс PersonAgeException предназначен для исключений, связанных с возрастом пользователя.
В конструкторе PersonAgeException получаем три значения - собственное некорректное значение, которое послужило причиной исключения, а также минимальное и максимальное значения возраста.
class PersonAgeException(Exception): def __init__(self, age, minage, maxage): self.age = age self.minage = minage self.maxage = maxage def __str__(self): return f"Недопустимое значение: . " \ f"Возраст должен быть в диапазоне от до "
В функции __str__ определяем текстовое представление класса - по сути сообщение об ошибке.
В конструкторе класса Persoon проверяем переданное для возраста пользователя значение. И если это значение не соответствует определенному диапазону, то генерируем исключение типа PersonAgeException:
raise PersonAgeException(age, minage, maxage)
При применении класса Person нам следует учитывать, что конструктор класса может сгенерировать исключение при передаче некорректного значения. Поэтому создание объектов Person обертывается в конструкцию try..except:
try: tom = Person("Tom", 37) tom.display_info() # Имя: Tom Возраст: 37 bob = Person("Bob", -23) # генерируется исключение типа PersonAgeException bob.display_info() except PersonAgeException as e: print(e) # Недопустимое значение: -23. Возраст должен быть в диапазоне от 1 до 110
И если при вызове конструктора Person будет сгенерировано исключение типа PersonAgeException, то управление программой перейдет к блоку except, который обрабатывает исключения типа PersonAgeException в виде вывода информации об исключении на консоль.
raise
Для принудительной генерации исключения используется инструкция raise.
Самый простой пример работы с raise может выглядеть так.
try: raise Exception("Some exception") except Exception as e: print("Exception exception " + str(e))
Таким образом, можно "вручную" вызывать исключения при необходимости.
Или перевызвать последнее исключение, вызвав raise без параметров.
Информация в исключении
Обычно достаточно перехватить исключение и реагировать на сам факт его появления.
args - это кортеж составных частей исключения. В него можно добавить свою информацию.
(a,b,c) = d
может породить исключение
ValueError: unpack list of wrong size
Сразу возникает вопрос - а что в переменной d, что не удалось его распаковать. Добавим значение переменной d в исключение и raise обновленное исключение.
try: a, b, c = d except Exception as e: e.args += (d,) raise
Иногда нужно логировать исключение или обрабатывать дополнительную информацию.
import traceback import sys def foo(a): x = 5 / a print(x, a) try: foo(5) foo(0) # на 0 делить нельзя foo(7) except ZeroDivisionError as e: # если e не нужно, то as e не пишем print('Поймали исключение!') print(e) # печать 'division by zero' print('-'*60) traceback.print_exc(file=sys.stdout) # печать stacktrace print('-'*60) print('После блока обработки исключений')
1.0 5 Поймали исключение! division by zero ------------------------------------------------------------ Traceback (most recent call last): File "1try.py", line 10, in foo(0) # на 0 делить нельзя File "1try.py", line 5, in foo x = 5 / a ZeroDivisionError: division by zero ------------------------------------------------------------ После блока обработки исключений
Напечатать только сообщение исключения
str(e) - просто преобразуйте исключение к строке.
Напечатать stacktrace исключения
Используйте функцию traceback.print_exc()
Тип, значение и traceback
Используйте функцию sys.exc_info()
exc_type, exc_value, exc_traceback = sys.exc_info()
Пример, как все это используется
Сохраните программу в файл tb.py и запустите ее.
import sys, traceback def lumberjack(): bright_side_of_death() def bright_side_of_death(): return tuple()[0] try: lumberjack() except IndexError: exc_type, exc_value, exc_traceback = sys.exc_info() print("*** print_tb:") traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) print("*** print_exception:") # exc_type below is ignored on 3.5 and later traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout) print("*** print_exc:") traceback.print_exc(limit=2, file=sys.stdout) print("*** format_exc, first and last line:") formatted_lines = traceback.format_exc().splitlines() print(formatted_lines[0]) print(formatted_lines[-1]) print("*** format_exception:") # exc_type below is ignored on 3.5 and later print(repr(traceback.format_exception(exc_type, exc_value, exc_traceback))) print("*** extract_tb:") print(repr(traceback.extract_tb(exc_traceback))) print("*** format_tb:") print(repr(traceback.format_tb(exc_traceback))) print("*** tb_lineno:", exc_traceback.tb_lineno)
*** print_tb: File "tb.py", line 10, in lumberjack() *** print_exception: Traceback (most recent call last): File "tb.py", line 10, in lumberjack() File "tb.py", line 4, in lumberjack bright_side_of_death() IndexError: tuple index out of range *** print_exc: Traceback (most recent call last): File "tb.py", line 10, in lumberjack() File "tb.py", line 4, in lumberjack bright_side_of_death() IndexError: tuple index out of range *** format_exc, first and last line: Traceback (most recent call last): IndexError: tuple index out of range *** format_exception: ['Traceback (most recent call last):\n', ' File "tb.py", line 10, in \n lumberjack()\n', ' File "tb.py", line 4, in lumberjack\n bright_side_of_death()\n', ' File "tb.py", line 7, in bright_side_of_death\n return tuple()[0]\n', 'IndexError: tuple index out of range\n'] *** extract_tb: [10 in >, 4 in lumberjack>, 7 in bright_side_of_death>] *** format_tb: [' File "tb.py", line 10, in \n lumberjack()\n', ' File "tb.py", line 4, in lumberjack\n bright_side_of_death()\n', ' File "tb.py", line 7, in bright_side_of_death\n return tuple()[0]\n'] *** tb_lineno: 10
results matching " "
No results matching " "
Перед дозаписью файла, скрипт должен выбросить случайное исключение и завершиться
Вот набросал, и работает как-то рандомно, то бросает исключения то нет и потом сам ругается на:
raise random.choice(errors) TypeError: exceptions must derive from BaseException
Запрашиваем у пользователя число до тех пор, пока сумма этих чисел не станет больше либо равна 777.
Каждое введённое число при этом дозаписывается в файл out_file.txt
Перед дозаписью с вероятностью 1 к 13 выбрасывает пользователю случайное исключение и завершается. Пример:
Введите число: 10 Введите число: 500 Введите число: 200 Введите число: 67 Вы успешно выполнили условие для выхода из порочного цикла! Содержимое файла out_file.txt: 10 500 200 67
Введите число: 10 Введите число: 500 Вас постигла неудача! Содержимое файла out_file.txt: 10
