43. Обработка исключений (try/except) в Python
В предыдущем уроке вы познакомились с циклами while/else и for/else . В этом уроке разберем, как обрабатывать исключения, вызываемые интерпретатором Python. Уверен, вы писали код, который отлично работал, но при вводе определенных данных, например, строки вместо числа, программа останавливалась и выдавала ошибку. Это генерировалось исключение и, так как вы его не обрабатывали, работа программы прекращалась.
Давайте целенаправленно вызовем исключение ZeroDivisionError . Если вы разделите любое число на ноль, то получите подобную ошибку:
Traceback (most recent call last):
File "C:/test.py", line 1, in
print(5/0)
ZeroDivisionError: division by zero
Этим сообщением интерпретатор Python нам говорит об ошибке в первой строке файла test.py , а точнее, в выражении print(5/0) . В последней строке ошибки содержится название исключения и ее краткое описание.
Иерархия исключений в Python
Теперь кратко рассмотрим некоторые из исключений, которые могут возникнуть в языке Python. Сразу оговорюсь, нет необходимости запоминать все эти исключения, так как всегда их можно посмотреть в справочнике или здесь. С другой стороны, знание возможных ошибок поможет их предвидеть и не допустить. Материал этого раздела находится под спойлером, так как получился объемным.
BaseException – это базовое исключение, от него берут начало все остальные исключения.
В базовое исключение входят:
- GeneratorExit : будет вызвано в случае применения метода close , объекта generator .
- SystemExit : будет вызвано функцией sys.exit в случае выхода из программы.
- KeyboardInterrupt : будет вызвано, если пользователь нажал сочетание клавиш Ctrl + C в консоли.
- Exception : в отличие от предыдущих, это группа не системных исключений, а «пользовательских», если так можно выразиться.
Исключения Exception
Группа исключений Exception подразделяется на следующие:
Группа исключений ArithmeticError
Исключения, связанные с арифметическими ошибками. Исключение FloatingPointError связано с выполнением неудачной операции с плавающей точкой. Исключение OverflowError возникает, когда результат арифметической операции слишком большой и не может быть представлен. Исключение ZeroDivisionError возникает из-за деления на ноль. Для примера, следующий код не будет работать, так как будет вызвано исключение OverflowError :
pi = 0
for k in range(350):
pi += 16.**k
print(pi)
Исключение AssertionError
Вызывается, если результат проверки инструкции assert является ложным. Например:
assert 1 == 0
К инструкции assert мы еще вернемся.
Исключение AttributeError
Возникает, если у объекта нет такого атрибута или в случае ошибки доступа к нему. Например:
a = 10
a.atr
Так как переменная (объект) a не имеет атрибута atr , то будет вызвано исключение:
Traceback (most recent call last):
File "C:/test.py", line 2, in
a.atr()
AttributeError: 'int' object has no attribute 'atr'
Исключение BufferError
Возникает, когда операция с буфером не может быть выполнена по тем или иным причинам. Например, буфер не доступен для записи, так как работает только в режиме «для чтения».
Исключение EOFError
Возникает, когда программа обнаруживает конец файла, то есть маркер EOF в процессе считывания данных.
Исключение ImportError
Возникает, если вы попробуете подключить несуществующий модуль при помощи инструкции import или отсутствующий атрибут.
Группа исключений LookupError
Исключение IndexError вызывается, если используемый индекс выходит за диапазон, например, обратимся к четвертому списка из трех элементов:
a = [1, 2, 3]
a[3]
Исключение KeyError возникает, если вы обратились к несуществующему ключу:
a =
a['4']
Исключение MemoryError
Возникает, если закончилась память при выполнении операции.
Исключение NameError
Возникает, если не найдено имя переменной, функции или другого объекта, например:
print(a)
Traceback (most recent call last):
File "C:/test.py", line 1, in
print(a)
NameError: name 'a' is not defined
Группа исключений OSError
Большая по своему составу группа исключений, связанных с системой.
В нее входит исключение BlockingIOError , которое поднимается, когда блокируется ввод/вывод. Исключение ChildProcessError возникает при неудачной операции с дочерним процессом. Исключение ConnectionError является еще одной группой, связанной с проблемами подключения. Исключение FileExistsError будет поднято, если вы попытаетесь создать уже существующий файл или директорию. Исключение FileNotFoundError возникнет, если вы обратитесь к несуществующему файлу или директории. Если системный вызов был прерван каким-либо входящим сигналом, то поднимется исключение InterruptedError . Исключение IsADirectoryError возникнет, если интерпретатор Python ожидал файл, но это оказалась директория. Исключение NotADirectoryError является противоположным предыдущему. Если возникли проблемы с правами доступа, то поднимется исключение PermissionError . Исключение ProcessLookupError возникнет, если такого процесса не существует, а TimeoutError , если закончилось время ожидания.
Исключение ReferenceError
Исключение ReferenceError поднимается в случае обращения к атрибуту объекта с помощью слабой ссылки. Тема про слабые ссылки не такая простая, но, возможно, мы к ней вернемся. Если кратко: слабые ссылки нужны для организации хэш-таблиц из «увесистых» объектов. За работу слабых ссылок отвечает модуль weakref .
Исключение RuntimeError
Такое исключение возникает в связи с ошибкой времени исполнения не попадающей под другие категории. До версии Python 3.5. под это исключение попадала бесконечная рекурсия, однако сейчас под это выделен новый тип исключений: RecursionError .
Исключение StopIteration
Поднимается функцией next , если в итераторе закончились элементы. Про итераторы мы поговорим в последующих уроках.
Исключение SyntaxError
Наверное, с исключением SyntaxError вы будете встречаться чаще всего. Оно поднимается парсером в случае нахождения синтаксических ошибок в исходном коде.
Исключение SystemError
Поднимается, когда возникают внутренние ошибки на уровне интерпретатора.
Исключение TypeError
Исключение TypeError поднимается при попытке обработки объекта, для которого такие действия неприменимы. Например, сложение строки и числа:
print('Hello' + 20)
Если вы забыли, что язык Python является сильно типизированным, то вернитесь к уроку про типы данных.
Исключение ValueError
Возникает, когда в функцию передан неправильный аргумент, т.е. тип переданного аргумента правильный, а значение не поддерживается.
Группа исключений Warning
Исключения группы Warning связаны с различными категориями предупреждений.
Конструкция try/except в Python
Настало время перейти к конструкции, которая позволяет «отлавливать» исключения в Python. Предположим, у нас есть программа, на вход которой поступает два числа, после чего одно делится на другое.
a, b = int(input()), int(input())
print(a / b)
Вы уже знаете, что при определенных входных данных, программа завершится. Конечно, от деления на ноль можно защититься при помощи условной конструкции if:
a, b = int(input()), int(input())
if (b != 0):
print(a / b)
else:
print("b равно 0")
Перепишем этот пример с использованием инструкции try/except :
try:
a, b = int(input()), int(input())
print(a / b)
except ZeroDivisionError:
print("b равно 0")
Как же работает инструкция try/except ? Сначала будет выполнено выражение, которое расположено в блоке try . Если внутри блока try не возникло никаких исключений, то блок except пропускается и выполняется дальнейший код.
Если все же внутри блока try возникло исключение, то оставшийся код в этом блоке пропускается и выполняется код в блоке except и управление передается следующей инструкции после этого блока. Например, следующая программа завершится корректно, даже при делении на ноль:
try:
a, b = int(input()), int(input())
print(a / b)
except ZeroDivisionError:
print("b равно 0")
print("Конец программы")
Однако если в блоке try поднялось исключение, тип которого не указан в блоке except, программа прервется и будет выдана ошибка. Блоков except может быть несколько:
try:
a, b = int(input()), int(input())
print(a / b)
except ZeroDivisionError:
print("b равно 0")
except ValueError:
print("Введите число")
Исключение ValueError возникнет, если вы введете, например, строку вместо числа. Если необходимо выполнить одни и те же действия при разных типах исключений, то можно их объединить в одном блоке except .
try:
a, b = int(input()), int(input())
print(a / b)
except (ZeroDivisionError, ValueError):
print("Ошибка в работе программы.")
Конструкция try/except поддерживает возможность перехватывать все исключения, для этого можно не указывать тип:
try:
a, b = int(input()), int(input())
print(a / b)
except:
print("Ошибка в работе программы.")
Конструкция try/except в Python имеет еще два опционных блока: finally и else . Блок finally выполняется всегда, независимо от того, было исключение или нет. Снова может показаться, что это бесполезный блок, ведь если except будет ловить все ошибки, то последующий код будет без проблем выполняться. Приведу пример, когда блок finally действительно необходим:
import sys
try:
while True:
pass
except KeyboardInterrupt:
print('Ctrl + C')
sys.exit()
finally:
print("Выполнение чего-то важного")
print("Конец программы")
Программа будет закрыта только при помощи сочетания клавиш Ctrl + C . Запустите пример и убедитесь что последний print() будет пропущен. А что если перед закрытием программы при помощи сочетания клавиш Ctrl + C необходимо сохранить пользовательскую информацию? Блок finally в данном случае вас выручит.
Опционный блок else в конструкции try/except нужен, чтобы выполнить необходимые инструкции только в том случае, если исключения не вызывались. Далее пример:
try:
a, b = int(input()), int(input())
print(a / b)
except:
print("Ошибка в работе программы.")
else:
print("Конец программы")
Ключевое слово as в Python
Ключевое слово as в контексте исключений может помочь узнать, какое именно исключение было поднято.
try:
a, b = int(input()), int(input())
print(a / b)
except Exception as e:
print(f"Исключение: ")
print(f"Что случилось: ")
В этом примере мы используем f-строки и функцию type(). Если про них забыли, то вернитесь к этим урокам чтобы освежить в памяти. К ключевому слову as мы еще вернемся, когда будем работать с файлами.
Ключевое слово raise в Python
Иногда необходимо сгенерировать исключение, можно использовать ключевое слово raise :
try:
a, b = int(input()), int(input())
if (b == 0):
raise ZeroDivisionError
print(a / b)
except ZeroDivisionError:
print("Деление на ноль")
Или вы можете вызвать свое исключение в виде текстового сообщения:
try:
a, b = int(input()), int(input())
if (b == 0):
raise ValueError("Деление на ноль")
print(a / b)
except Exception as e:
print(e)
Утверждение assert в Python
Утверждение assert в Python используется в отладочных целях, а именно, для проверки истинности утверждений. Если результат проверки будет ложным, то будет возбуждено исключение AssertionError .
Инструкцию assert рекомендуется использовать для проверки ситуаций, которые не должны происходить, то есть те, которые нельзя обработать.
Заключение
В процессе программирования ошибок не избежать. Ошибки могут появиться как из-за неправильного ввода данных пользователем, так и по многим другим причинам, включая невнимательность программиста.
В этом уроке мы научились обрабатывать ошибки в Python при помощи конструкций try/except , try/except/else и try/except/finally . Узнали, как возбудить свою ошибку при помощи ключевого слова raise . Кратко узнали о ключевом слове as и утверждении assert .
Перед обработкой исключений, оцените как проще и эффективней предупредить ошибку: использовать try/except или обычную условную конструкцию if .
Обработка исключений в Python (try except)
Программа, написанная на языке Python, останавливается сразу как обнаружит ошибку. Ошибки могут быть (как минимум) двух типов:
- Синтаксические ошибки — возникают, когда написанное выражение не соответствует правилам языка (например, написана лишняя скобка);
- Исключения — возникают во время выполнения программы (например, при делении на ноль).
Синтаксические ошибки исправить просто (если вы используете IDE, он их подсветит). А вот с исключениями всё немного сложнее — не всегда при написании программы можно сказать возникнет или нет в данном месте исключение. Чтобы приложение продолжило работу при возникновении проблем, такие ошибки нужно перехватывать и обрабатывать с помощью блока try/except .
Как устроен механизм исключений
В Python есть встроенные исключения, которые появляются после того как приложение находит ошибку. В этом случае текущий процесс временно приостанавливается и передает ошибку на уровень вверх до тех пор, пока она не будет обработано. Если ошибка не будет обработана, программа прекратит свою работу (а в консоли мы увидим Traceback с подробным описанием ошибки).
♂️ Пример : напишем скрипт, в котором функция ожидает число, а мы передаём сроку (это вызовет исключение «TypeError»):
def b(value): print(«-> b») print(value + 1) # ошибка тут def a(value): print(«-> a») b(value) a(«10») > -> a > -> b > Traceback (most recent call last): > File «test.py», line 11, in > a(«10») > File «test.py», line 8, in a > b(value) > File «test.py», line 3, in b > print(value + 1) > TypeError: can only concatenate str (not «int») to str
В данном примере мы запускаем файл » test.py » (через консоль). Вызывается функция » a «, внутри которой вызывается функция » b «. Все работает хорошо до сточки print(value + 1) . Тут интерпретатор понимает, что нельзя конкатенировать строку с числом, останавливает выполнение программы и вызывает исключение «TypeError».
Далее ошибка передается по цепочке в обратном направлении: » b » → » a » → » test.py «. Так как в данном примере мы не позаботились обработать эту ошибку, вся информация по ошибке отобразится в консоли в виде Traceback.
Traceback (трассировка) — это отчёт, содержащий вызовы функций, выполненные в определенный момент. Трассировка помогает узнать, что пошло не так и в каком месте это произошло.
Traceback лучше читать снизу вверх ↑
В нашем примере Traceback содержится следующую информацию (читаем снизу вверх):
- TypeError — тип ошибки (означает, что операция не может быть выполнена с переменной этого типа);
- can only concatenate str (not «int») to str — подробное описание ошибки (конкатенировать можно только строку со строкой);
- Стек вызова функций (1-я линия — место, 2-я линия — код). В нашем примере видно, что в файле «test.py» на 11-й линии был вызов функции «a» со строковым аргументом «10». Далее был вызов функции «b». print(value + 1) это последнее, что было выполнено — тут и произошла ошибка.
- most recent call last — означает, что самый последний вызов будет отображаться последним в стеке (в нашем примере последним выполнился print(value + 1) ).
В Python ошибку можно перехватить, обработать, и продолжить выполнение программы — для этого используется конструкция try . except . .
Как обрабатывать исключения в Python (try except)
В Python исключения обрабатываются с помощью блоков try/except . Для этого операция, которая может вызвать исключение, помещается внутрь блока try . А код, который должен быть выполнен при возникновении ошибки, находится внутри except .
Например, вот как можно обработать ошибку деления на ноль:
try: a = 7 / 0 except: print(‘Ошибка! Деление на 0’)
Здесь в блоке try находится код a = 7 / 0 — при попытке его выполнить возникнет исключение и выполнится код в блоке except (то есть будет выведено сообщение «Ошибка! Деление на 0»). После этого программа продолжит свое выполнение.
PEP 8 рекомендует, по возможности, указывать конкретный тип исключения после ключевого слова except (чтобы перехватывать и обрабатывать конкретные исключения):
try: a = 7 / 0 except ZeroDivisionError: print(‘Ошибка! Деление на 0’)
Однако если вы хотите перехватывать все исключения, которые сигнализируют об ошибках программы, используйте тип исключения Exception :
try: a = 7 / 0 except Exception: print(‘Любая ошибка!’)
As — сохраняет ошибку в переменную
Перехваченная ошибка представляет собой объект класса, унаследованного от «BaseException». С помощью ключевого слова as можно записать этот объект в переменную, чтобы обратиться к нему внутри блока except :
try: file = open(‘ok123.txt’, ‘r’) except FileNotFoundError as e: print(e) > [Errno 2] No such file or directory: ‘ok123.txt’
В примере выше мы обращаемся к объекту класса «FileNotFoundError» (при выводе на экран через print отобразится строка с полным описанием ошибки).
У каждого объекта есть поля, к которым можно обращаться (например если нужно логировать ошибку в собственном формате):
import datetime now = datetime.datetime.now().strftime(«%d-%m-%Y %H:%M:%S») try: file = open(‘ok123.txt’, ‘r’) except FileNotFoundError as e: print(f» [FileNotFoundError]: , filename: «) > 20-11-2021 18:42:01 [FileNotFoundError]: No such file or directory, filename: ok123.txt
Finally — выполняется всегда
При обработке исключений можно после блока try использовать блок finally . Он похож на блок except , но команды, написанные внутри него, выполняются обязательно. Если в блоке try не возникнет исключения, то блок finally выполнится так же, как и при наличии ошибки, и программа возобновит свою работу.
Обычно try/except используется для перехвата исключений и восстановления нормальной работы приложения, а try/finally для того, чтобы гарантировать выполнение определенных действий (например, для закрытия внешних ресурсов, таких как ранее открытые файлы).
В следующем примере откроем файл и обратимся к несуществующей строке:
file = open(‘ok.txt’, ‘r’) try: lines = file.readlines() print(lines[5]) finally: file.close() if file.closed: print(«файл закрыт!») > файл закрыт! > Traceback (most recent call last): > File «test.py», line 5, in > print(lines[5]) > IndexError: list index out of range
Даже после исключения «IndexError», сработал код в секции finally , который закрыл файл.
p.s. данный пример создан для демонстрации, в реальном проекте для работы с файлами лучше использовать менеджер контекста with.
Также можно использовать одновременно три блока try/except/finally . В этом случае:
- в try — код, который может вызвать исключения;
- в except — код, который должен выполниться при возникновении исключения;
- в finally — код, который должен выполниться в любом случае.
Else — выполняется когда исключение не было вызвано
Иногда нужно выполнить определенные действия, когда код внутри блока try не вызвал исключения. Для этого используется блок else .
Допустим нужно вывести результат деления двух чисел и обработать исключения в случае попытки деления на ноль:
b = int(input(‘b = ‘)) c = int(input(‘c = ‘)) try: a = b / c except ZeroDivisionError: print(‘Ошибка! Деление на 0’) else: print(f»a = «) > b = 10 > c = 1 > a = 10.0
В этом случае, если пользователь присвоит переменной » с » ноль, то появится исключение и будет выведено сообщение «‘Ошибка! Деление на 0′», а код внутри блока else выполняться не будет. Если ошибки не будет, то на экране появятся результаты деления.
Несколько блоков except
В программе может возникнуть несколько исключений, например:
- Ошибка преобразования введенных значений к типу float («ValueError»);
- Деление на ноль («ZeroDivisionError»).
В Python, чтобы по-разному обрабатывать разные типы ошибок, создают несколько блоков except :
try: b = float(input(‘b = ‘)) c = float(input(‘c = ‘)) a = b / c except ZeroDivisionError: print(‘Ошибка! Деление на 0’) except ValueError: print(‘Число введено неверно’) else: print(f»a = «) > b = 10 > c = 0 > Ошибка! Деление на 0 > b = 10 > c = питон > Число введено неверно
Теперь для разных типов ошибок есть свой обработчик.
Несколько типов исключений в одном блоке except
Можно также обрабатывать в одном блоке except сразу несколько исключений. Для этого они записываются в круглых скобках, через запятую сразу после ключевого слова except . Чтобы обработать сообщения «ZeroDivisionError» и «ValueError» в одном блоке записываем их следующим образом:
try: b = float(input(‘b = ‘)) c = float(input(‘c = ‘)) a = b / c except (ZeroDivisionError, ValueError) as er: print(er) else: print(‘a = ‘, a)
При этом переменной er присваивается объект того исключения, которое было вызвано. В результате на экран выводятся сведения о конкретной ошибке.
Raise — самостоятельный вызов исключений
Исключения можно генерировать самостоятельно — для этого нужно запустить оператор raise .
min = 100 if min > 10: raise Exception(‘min must be less than 10’) > Traceback (most recent call last): > File «test.py», line 3, in > raise Exception(‘min value must be less than 10’) > Exception: min must be less than 10
Перехватываются такие сообщения точно так же, как и остальные:
min = 100 try: if min > 10: raise Exception(‘min must be less than 10’) except Exception: print(‘Моя ошибка’) > Моя ошибка
Кроме того, ошибку можно обработать в блоке except и пробросить дальше (вверх по стеку) с помощью raise :
min = 100 try: if min > 10: raise Exception(‘min must be less than 10’) except Exception: print(‘Моя ошибка’) raise > Моя ошибка > Traceback (most recent call last): > File «test.py», line 5, in > raise Exception(‘min must be less than 10’) > Exception: min must be less than 10
Как пропустить ошибку
Иногда ошибку обрабатывать не нужно. В этом случае ее можно пропустить с помощью pass :
try: a = 7 / 0 except ZeroDivisionError: pass
Исключения в lambda функциях
Обрабатывать исключения внутри lambda функций нельзя (так как lambda записывается в виде одного выражения). В этом случае нужно использовать именованную функцию.
20 типов встроенных исключений в Python
Иерархия классов для встроенных исключений в Python выглядит так:
BaseException SystemExit KeyboardInterrupt GeneratorExit Exception ArithmeticError AssertionError . . . ValueError Warning
Все исключения в Python наследуются от базового BaseException :
- SystemExit — системное исключение, вызываемое функцией sys.exit() во время выхода из приложения;
- KeyboardInterrupt — возникает при завершении программы пользователем (чаще всего при нажатии клавиш Ctrl+C);
- GeneratorExit — вызывается методом close объекта generator ;
- Exception — исключения, которые можно и нужно обрабатывать (предыдущие были системными и их трогать не рекомендуется).
От Exception наследуются:
1 StopIteration — вызывается функцией next в том случае если в итераторе закончились элементы;
2 ArithmeticError — ошибки, возникающие при вычислении, бывают следующие типы:
- FloatingPointError — ошибки при выполнении вычислений с плавающей точкой (встречаются редко);
- OverflowError — результат вычислений большой для текущего представления (не появляется при операциях с целыми числами, но может появиться в некоторых других случаях);
- ZeroDivisionError — возникает при попытке деления на ноль.
3 AssertionError — выражение, используемое в функции assert неверно;
4 AttributeError — у объекта отсутствует нужный атрибут;
5 BufferError — операция, для выполнения которой требуется буфер, не выполнена;
6 EOFError — ошибка чтения из файла;
7 ImportError — ошибка импортирования модуля;
8 LookupError — неверный индекс, делится на два типа:
- IndexError — индекс выходит за пределы диапазона элементов;
- KeyError — индекс отсутствует (для словарей, множеств и подобных объектов);
9 MemoryError — память переполнена;
10 NameError — отсутствует переменная с данным именем;
11 OSError — исключения, генерируемые операционной системой:
- ChildProcessError — ошибки, связанные с выполнением дочернего процесса;
- ConnectionError — исключения связанные с подключениями (BrokenPipeError, ConnectionResetError, ConnectionRefusedError, ConnectionAbortedError);
- FileExistsError — возникает при попытке создания уже существующего файла или директории;
- FileNotFoundError — генерируется при попытке обращения к несуществующему файлу;
- InterruptedError — возникает в том случае если системный вызов был прерван внешним сигналом;
- IsADirectoryError — программа обращается к файлу, а это директория;
- NotADirectoryError — приложение обращается к директории, а это файл;
- PermissionError — прав доступа недостаточно для выполнения операции;
- ProcessLookupError — процесс, к которому обращается приложение не запущен или отсутствует;
- TimeoutError — время ожидания истекло;
12 ReferenceError — попытка доступа к объекту с помощью слабой ссылки, когда объект не существует;
13 RuntimeError — генерируется в случае, когда исключение не может быть классифицировано или не подпадает под любую другую категорию;
14 NotImplementedError — абстрактные методы класса нуждаются в переопределении;
15 SyntaxError — ошибка синтаксиса;
16 SystemError — сигнализирует о внутренне ошибке;
17 TypeError — операция не может быть выполнена с переменной этого типа;
18 ValueError — возникает когда в функцию передается объект правильного типа, но имеющий некорректное значение;
19 UnicodeError — исключение связанное с кодирование текста в unicode , бывает трех видов:
- UnicodeEncodeError — ошибка кодирования;
- UnicodeDecodeError — ошибка декодирования;
- UnicodeTranslateError — ошибка перевода unicode .
20 Warning — предупреждение, некритическая ошибка.
Посмотреть всю цепочку наследования конкретного типа исключения можно с помощью модуля inspect :
Подробное описание всех классов встроенных исключений в Python смотрите в официальной документации .
Как создать свой тип Exception
В Python можно создавать свои исключения. При этом есть одно обязательное условие: они должны быть потомками класса Exception :
class MyError(Exception): def __init__(self, text): self.txt = text try: raise MyError(‘Моя ошибка’) except MyError as er: print(er) > Моя ошибка
С помощью try/except контролируются и обрабатываются ошибки в приложении. Это особенно актуально для критически важных частей программы, где любые «падения» недопустимы (или могут привести к негативным последствиям). Например, если программа работает как «демон», падение приведет к полной остановке её работы. Или, например, при временном сбое соединения с базой данных, программа также прервёт своё выполнение (хотя можно было отловить ошибку и попробовать соединиться в БД заново).
Вместе с try/except можно использовать дополнительные блоки. Если использовать все блоки описанные в статье, то код будет выглядеть так:
try: # попробуем что-то сделать except (ZeroDivisionError, ValueError) as e: # обрабатываем исключения типа ZeroDivisionError или ValueError except Exception as e: # исключение не ZeroDivisionError и не ValueError # поэтому обрабатываем исключение общего типа (унаследованное от Exception) # сюда не сходят исключения типа GeneratorExit, KeyboardInterrupt, SystemExit else: # этот блок выполняется, если нет исключений # если в этом блоке сделать return, он не будет вызван, пока не выполнился блок finally finally: # этот блок выполняется всегда, даже если нет исключений else будет проигнорирован # если в этом блоке сделать return, то return в блоке
Подробнее о работе с исключениями в Python можно ознакомиться в официальной документации .
Работа с исключениями try/except/else/finally#
Если вы повторяли примеры, которые использовались ранее, то наверняка были ситуации, когда выскакивала ошибка. Скорее всего, это была ошибка синтаксиса, когда не хватало, например, двоеточия.
Как правило, Python довольно понятно реагирует на подобные ошибки, и их можно исправить.
Тем не менее, даже если код синтаксически написан правильно, могут возникать ошибки. В Python эти ошибки называются исключения (exceptions).
In [1]: 2/0 ----------------------------------------------------- ZeroDivisionError: division by zero In [2]: 'test' + 2 ----------------------------------------------------- TypeError: must be str, not int
В данном случае возникло два исключения: ZeroDivisionError и TypeError.
Чаще всего можно предсказать, какого рода исключения возникнут во время исполнения программы.
Например, если программа на вход ожидает два числа, а на выходе выдает их сумму, а пользователь ввел вместо одного из чисел строку, появится ошибка TypeError, как в примере выше.
Python позволяет работать с исключениями. Их можно перехватывать и выполнять определенные действия в том случае, если возникло исключение.
Когда в программе возникает исключение, она сразу завершает работу.
Для работы с исключениями используется конструкция try/except :
In [3]: try: . : 2/0 . : except ZeroDivisionError: . : print("You can't divide by zero") . : You can't divide by zero
Конструкция try работает таким образом:
- сначала выполняются выражения, которые записаны в блоке try
- если при выполнения блока try не возникло никаких исключений, блок except пропускается, и выполняется дальнейший код
- если во время выполнения блока try в каком-то месте возникло исключение, оставшаяся часть блока try пропускается
- если в блоке except указано исключение, которое возникло, выполняется код в блоке except
- если исключение, которое возникло, не указано в блоке except, выполнение программы прерывается и выдается ошибка
Обратите внимание, что строка Cool! в блоке try не выводится:
In [4]: try: . : print("Let's divide some numbers") . : 2/0 . : print('Cool!') . : except ZeroDivisionError: . : print("You can't divide by zero") . : Let's divide some numbers You can't divide by zero
В конструкции try/except может быть много except, если нужны разные действия в зависимости от типа ошибки.
Например, скрипт divide.py делит два числа введенных пользователем:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except ValueError: print("Пожалуйста, вводите только числа") except ZeroDivisionError: print("На ноль делить нельзя")
Примеры выполнения скрипта:
$ python divide.py Введите первое число: 3 Введите второе число: 1 Результат: 3 $ python divide.py Введите первое число: 5 Введите второе число: 0 На ноль делить нельзя $ python divide.py Введите первое число: qewr Введите второе число: 3 Пожалуйста, вводите только числа
В данном случае исключение ValueError возникает, когда пользователь ввел строку вместо числа, во время перевода строки в число.
Исключение ZeroDivisionError возникает в случае, если второе число было равным 0.
Если нет необходимости выводить различные сообщения на ошибки ValueError и ZeroDivisionError, можно сделать так (файл divide_ver2.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except (ValueError, ZeroDivisionError): print("Что-то пошло не так. ")
$ python divide_ver2.py Введите первое число: wer Введите второе число: 4 Что-то пошло не так. $ python divide_ver2.py Введите первое число: 5 Введите второе число: 0 Что-то пошло не так.
В блоке except можно не указывать конкретное исключение или исключения. В таком случае будут перехватываться все исключения.
Это делать не рекомендуется!
try/except/else#
В конструкции try/except есть опциональный блок else. Он выполняется в том случае, если не было исключения.
Например, если необходимо выполнять в дальнейшем какие-то операции с данными, которые ввел пользователь, можно записать их в блоке else (файл divide_ver3.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так. ") else: print("Результат в квадрате: ", result**2)
$ python divide_ver3.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 $ python divide_ver3.py Введите первое число: werq Введите второе число: 3 Что-то пошло не так.
try/except/finally#
Блок finally — это еще один опциональный блок в конструкции try. Он выполняется всегда, независимо от того, было ли исключение или нет.
Сюда ставятся действия, которые надо выполнить в любом случае. Например, это может быть закрытие файла.
Файл divide_ver4.py с блоком finally:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так. ") else: print("Результат в квадрате: ", result**2) finally: print("Вот и сказочке конец, а кто слушал - молодец.")
$ python divide_ver4.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: qwerewr Введите второе число: 3 Что-то пошло не так. Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: 4 Введите второе число: 0 Что-то пошло не так. Вот и сказочке конец, а кто слушал - молодец.
Когда использовать исключения#
Как правило, один и тот же код можно написать и с использованием исключений, и без них.
Например, этот вариант кода:
while True: a = input("Введите число: ") b = input("Введите второе число: ") try: result = int(a)/int(b) except ValueError: print("Поддерживаются только числа") except ZeroDivisionError: print("На ноль делить нельзя") else: print(result) break
Можно переписать таким образом без try/except (файл try_except_divide.py):
while True: a = input("Введите число: ") b = input("Введите второе число: ") if a.isdigit() and b.isdigit(): if int(b) == 0: print("На ноль делить нельзя") else: print(int(a)/int(b)) break else: print("Поддерживаются только числа")
Далеко не всегда аналогичный вариант без использования исключений будет простым и понятным.
Важно в каждой конкретной ситуации оценивать, какой вариант кода более понятный, компактный и универсальный — с исключениями или без.
Если вы раньше использовали какой-то другой язык программирования, есть вероятность, что в нём использование исключений считалось плохим тоном. В Python это не так. Чтобы немного больше разобраться с этим вопросом, посмотрите ссылки на дополнительные материалы в конце этого раздела.
raise#
Иногда в коде надо сгенерировать исключение, это можно сделать так:
raise ValueError("При выполнении команды возникла ошибка")
Встроенные исключения#
В Python есть много встроенных исключений, каждое из которых генерируется в определенной ситуации.
Например, TypeError обычно генерируется когда ожидался один тип данных, а передали другой
In [1]: "a" + 3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) ipython-input-1-5aa8a24e3e06> in module> ----> 1 "a" + 3 TypeError: can only concatenate str (not "int") to str
ValueError когда значение не соответствует ожидаемому:
In [2]: int("a") --------------------------------------------------------------------------- ValueError Traceback (most recent call last) ipython-input-2-d9136db7b558> in module> ----> 1 int("a") ValueError: invalid literal for int() with base 10: 'a'
Тонкости работы конструкции try/except/else/finally в Python
Подробный алгоритм обработки исключений со всеми нюансами
Для лучшего понимания этого раздела, рекомендуем ознакомиться с общими приемами обработки исключений и использованием инструкции finally .
В конструкция try/except/else/finally определяется обработчики исключений и/или код очистки для группы операторов.
Поведение инструкции except .
В инструкциях except указывается один или несколько обработчиков исключений. Если в коде между инструкциями try/except не возникает исключения, то обработчик исключений не выполняется. При возникновении исключения в коде между инструкциями try/except запускается поиск обработчика исключения. Этот поиск проверяет выражения во всех инструкциях except по очереди, пока не будет найден тот, который соответствует исключению.
Инструкция except без выражения, если она присутствует, должна быть последней. Она соответствует любому встроенному исключению.
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: # содержит выражение OSError as err print("OS error: ".format(err)) except ValueError: # содержит выражение ValueError print("Не удалось преобразовать данные в целое число.") except: # инструкция 'except' без выражения print("Непредвиденная ошибка:", sys.exc_info()[0])
Для инструкций except с выражением — выражение оценивается и сравнивается с брошенным исключением в коде, между инструкцией try и первым except . Выражение соответствует исключению в том случае, если результирующий объект выражения «совместим» с исключением. Объект выражения совместим с исключением, если это класс или базовый класс объекта исключения или кортеж, содержащий элемент, совместимый с исключением.
try: . except (OSError, ValueError): # инструкция 'except' содержит # кортеж возможных исключений .
Если исключению не соответствует выражению, указанному в инструкции except , то поиск обработчика исключения продолжается в окружающем коде и в стеке вызовов.
Если вычисление выражения в заголовке инструкции except вызывает исключение, то исходный поиск обработчика отменяется и начинается поиск нового исключения в окружающем коде и в стеке вызовов. Другими словами, такая ситуация обрабатывается так, как если бы вся цепочка инструкций try вызвала исключение.
Если найдено соответствующее выражение в инструкции except , то исключение назначается целевому объекту, указанному после ключевого слова as в этом выражении, если конечно as присутствует, далее выполняется блок кода выбранной инструкции except . Все инструкции except должны иметь исполняемый блок кода. Когда достигается конец выбранного блока, выполнение кода продолжается после выполнения всей инструкции try . Это означает, что если существуют два вложенных обработчика для одного и того же исключения и исключение возникает в предложении try внутреннего обработчика, то внешний обработчик не будет обрабатывать исключение.
Когда исключение было назначено с помощью выражения as target , то переменная target очищается в конце предложения except . Это как если бы:
except E as N: foo
был переведен на:
except E as N: try: foo finally: del N
Следовательно, если надо сослаться на исключение target после инструкции except , то необходимо исключению в target присвоить другой переменной. Исключения очищаются, так как с присоединенной к ним обратной трассировкой они образуют ссылочный цикл с кадром стека, сохраняя все локальные объекты в этом кадре живыми до следующей сборки мусора.
Перед выполнением блока кода инструкции except , сведения об исключении сохраняются в модуле sys и могут быть доступны через sys.exc_info() . Метод sys.exc_info() возвращает тройной кортеж, состоящий из класса исключения, экземпляра исключения и объекта трассировки, идентифицирующего точку в программе, где произошло исключение. Значение метода sys.exc_info() восстанавливаются до своих предыдущих значений (до вызова) при возврате из функции, которая обработала исключение.
Поведение необязательной инструкции else .
Необязательная инструкция else выполняется, только если поток управления покидает блок кода между try/except , не вызвав исключения и если не было выполнено ни одного оператора return , continue или break в этом блоке кода.
Исключения в блоке кода else не обрабатываются предыдущими инструкциями except .
Поведение необязательной инструкции finally .
Если в конструкции try присутствует инструкция finally , то она определяет обработчик ‘cleanup’ . Оператор try выполняет все инструкции, включая except и else . Если исключение возникает в любой из этих инструкции и не обрабатывается, то исключение временно сохраняется. При исполнении инструкции finally , если существует сохраненное исключение, оно повторно вызывается после выполнения блока кода инструкции finally . Если finally вызывает другое исключение, то сохраненное исключение устанавливается как контекст нового исключения. Если инструкция finally выполняет оператор return , break или continue , то сохраненное исключение отбрасывается:
def f(): try: 1/0 finally: return 42 >>> f() # 42
Информация об исключении недоступна программе во время выполнения инструкции finally .
Когда операторы return , break или continue выполняется в блоке кода инструкции try конструкции try/finally , блок кода инструкции finally также выполняется ‘на выходе.’
Возвращаемое значение функции определяется последним выполненным оператором return . Поскольку инструкция finally выполняется в любом случае, оператор return , выполняемый в блоке кода инструкции finally , всегда будет последним выполненным:
def foo(): try: return 'try' finally: return 'finally' >>> foo() # 'finally'
Дополнительную информацию об исключениях можно найти в материалах «Ошибки выполнения программы» и «Обработка исключений в Python», а информацию об использовании инструкции raise генерации исключений можно найти в материале «Инструкция raise в Python»
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Обработка/перехват исключений try/except
- Инструкция finally, очистка внешних ресурсов
- Тонкости работы конструкции try/except/else/finally
- Создание пользовательского класса исключения
- Обработка группы исключений, оператор except*
- Эффективная обработка исключений
- Инструкция raise, принудительный вызов исключений
- Отладочные утверждение assert
- Улучшения сообщений об ошибках 3.10
- Улучшения сообщений об ошибках 3.11
- Улучшения сообщений об ошибках 3.12
