Не пойманное исключение
def foo(a):
x = 5 / a
print(x, a)
foo(5)
foo(0) # на 0 делить нельзя
foo(7)
- foo(5) был вызван и выполнился.
- foo(0) был вызван, но выполнение всей программы прекратилось на строке x = 5 / a
- foo(7) не вызвался.
Получили:
1.0 5
Traceback (most recent call last):
File "nocatch.py", line 6, in <module>
foo(0) # на 0 делить нельзя
File "nocatch.py", line 2, in foo
x = 5 / a
ZeroDivisionError: division by zero
Это называется stacktrace.
Рассмотрим подробнее сообщение об ошибке. Напечатан стек вызова функций с указанием в каком файле на какой строке возникло исключение и код, который его породил.
ZeroDivisionError: division by zero
ZeroDivisionError - тип исключения. Исключение - это такой же объект, как и другие данные в программе.
division by zero - текст этого исключения. Зависит от типа.
Блок try - except
Попробуем поймать исключение:
def foo(a):
x = 5 / a
print(x, a)
try:
foo(5)
foo(0) # на 0 делить нельзя
foo(7)
except ZeroDivisionError as e:
print('Поймали исключение!')
print('После блока обработки исключений')
Получили:
1.0 5
Поймали исключение!
После блока обработки исключений
Видим, что при поимке исключения, программа может выполняться дальше.
foo(7) - не выполняется, так как это блок try - то что нужно выполнить до первого исключения.
print('После блока обработки исключений') - программа работает дальше, после окончания try-ecxept блока.
Как работает перехват исключений
- Вначале выполняется код, находящийся между операторами try и except.
- Если в ходе его выполнения исключения не произошло, то код в блоке except пропускается, а код в блоке try выполняется весь до конца.
- Если исключение происходит, то выполнение в рамках блока try прерывается и выполняется код в блоке except. При этом для оператора except можно указать, какие исключения можно обрабатывать в нем. При возникновении исключения, ищется именно тот блок except, который может обработать данное исключение.
- Если среди except блоков нет подходящего для обработки исключения, то оно передается наружу из блока try. В случае, если обработчик исключения так и не будет найден, то исключение будет необработанным (unhandled exception) и программа аварийно остановится.
Много разных исключений
Иногда код может породить исключения разных типов.
Если они обрабатываются одинаково, то перечислите их типы через запятую:
except (RuntimeError, TypeError, NameError):
pass
Если нужна разная обработка, то пишем много except блоков:
ecxept RuntimeError:
print('один случай')
ecxept TypeError:
print('второй случай')
ecxept NameError:
print('третий случай')
Примеры. Перечисление:
import traceback
import sys
def foo(a):
b = [1, 2, 3]
x = 5 / a
y = b[a]
print(x, a, y)
try:
foo(2)
# foo(0) # на 0 делить нельзя
foo(7)
except (ZeroDivisionError, IndexError) as e:
print('Поймали исключение!')
print(e)
print('-'*60)
traceback.print_exc(file=sys.stdout)
print('-'*60)
print('После блока обработки исключений')
Отдельные блоки:
import traceback
import sys
def foo(a):
b = [1, 2, 3]
x = 5 / a
y = b[a]
print(x, a, y)
try:
foo(2) # ok
foo(0) # на 0 делить нельзя
foo(7) # выход за границы списка
except ZeroDivisionError:
pass
except IndexError as e:
print('Поймали исключение!')
print(e)
print('-'*60)
traceback.print_exc(file=sys.stdout)
print('-'*60)
print('После блока обработки исключений')
Разные исключения ловим в разных местах
Вызываются функции bzz -> qqq -> foo.
В foo возникают исключения ZeroDivisionError и IndexError.
Поймаем исключение ZeroDivisionError в функции qqq, а IndexError в функции bzz.
import traceback
import sys
def foo(a):
b = [1, 2, 3]
x = 5 / a
y = b[a]
print(x, a, y)
def qqq(a):
try:
foo(a)
except ZeroDivisionError:
print('qqq: ZeroDivisionError')
traceback.print_exc(file=sys.stdout)
print('qqq: After try-ecxept block')
def bzz(a):
try:
qqq(a)
except IndexError:
print('bzz: IndexError')
traceback.print_exc(file=sys.stdout)
print('bzz: After try-ecxept block\n')
bzz(2)
# 2.5 2 3
# qqq: After try-ecxept block
# bzz: After try-ecxept block
bzz(0)
# qqq: ZeroDivisionError
# qqq: After try-ecxept block
# bzz: After try-ecxept block
bzz(7)
# bzz: IndexError
# bzz: After try-ecxept block
Обратите внимание на различие в stacktrace.
2.5 2 3
qqq: After try-ecxept block
bzz: After try-ecxept block
qqq: ZeroDivisionError
Traceback (most recent call last):
File "2_2try_tb.py", line 12, in qqq
foo(a)
File "2_2try_tb.py", line 6, in foo
x = 5 / a
ZeroDivisionError: division by zero
qqq: After try-ecxept block
bzz: After try-ecxept block
bzz: IndexError
Traceback (most recent call last):
File "2_2try_tb.py", line 20, in bzz
qqq(a)
File "2_2try_tb.py", line 12, in qqq
foo(a)
File "2_2try_tb.py", line 7, in foo
y = b[a]
IndexError: list index out of range
bzz: After try-ecxept block