Конструкция try - except для обработки исключений

Допустим, нам нужно написать функцию, которая возвращает частное от деления двух чисел; и ноль, если деление не удалось.

Кажется легко, верно? Давайте напишем код:

def division(x, y):
    return x / y

И вызовем его

>>> print(division(1, 0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in division
ZeroDivisionError: division by zero

Ошибка деления на ноль. Или как говорят, возникло исключение. Исключения можно избежать, если, например, до операции деления выполнить проверку на 0:

def division(x, y):
    if y == 0:
        return 0
    return x / y

А тогда ещё пример:

>>> print(division(1.5, 7 ** 512))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in division
OverflowError: int too large to convert to float

Если первый аргумент - число с плавающей точкой, а второй - большое целое число, то Python, пытаясь преобразовать его в число с плавающей точкой, терпит неудачу.

Можно, конечно, добавить ещё проверок. Но есть более простой способ, а именно обработать исключение с помощью конструкции try - except:

def division(x, y):
    if y == 0:
        return 0
    try:
        answer = x / y
    except OverflowError:
        answer = 0
    return answer

Программа пытается выполнить код в блоке try. Если получается, то далее выполнится то, что после блока. В нашей функции это возврат переменной answer.

Если не получается, то выполнение внутри блока try прерывается, а затем интерпретатор смотрит, что за исключение произошло. Если оно (либо его родитель) есть в одном из списков except, то выполнится блок except. Иначе возникнет исключение, то самое, которое было в блоке try.

Можно убрать проверку на 0, написав вместо неё обработку исключения в отдельном блоке except (и таких блоков может быть сколько угодно):

def division(x, y):
    try:
        answer = x / y
    except OverflowError:
        answer = 0
    except ZeroDivisionError:
        answer = 0
    return answer

записав имена исключения через запятую (только потому, что обработка обоих из них у нас одинакова):

def division(x, y):
    try:
        answer = x / y
    except (OverflowError, ZeroDivisionError):
        answer = 0
    return answer

или, поскольку OverflowError и ZeroDivisionError это подвид ArithmeticError, можно перехватить только его:

def division(x, y):
    try:
        answer = x / y
    except ArithmeticError:
        answer = 0
    return answer

try - except - else

Инструкция else в блоке try - except выполнится только, если при выполнении блока try не было исключения:

def division(x, y):
    try:
        answer = x / y
    except ArithmeticError:
        answer = 0
    else:
        print("Деление было выполнено штатно")
    return answer
Как и в случае с for - else, try - except - else не имеет ничего общего с выражением if - else. То, к какому блоку относится else, указывается уровнем отступа.

finally

Инструкции в блоке finally блока try - except выполнятся при любом исходе блока try. Даже если возникнет не отловленное исключение, перед выдачей ошибки выполнится блок finally:

def division(x, y):
    try:
        answer = x / y
    except OverflowError:
        answer = 0
    else:
        print("Деление было выполнено штатно")
    finally:
        print("Заходи в наш телеграм-канал (ссылка внизу)")
    return answer


print(division(2, 1))
print()
print(division(1.5, 7 ** 512))
print()
print(division(1, 0))

И вывод данной программы:

Деление было выполнено штатно
Заходи в наш телеграм-канал (ссылка внизу)
2.0

Заходи в наш телеграм-канал (ссылка внизу)
0

Заходи в наш телеграм-канал (ссылка внизу)
Traceback (most recent call last):
  File "C:\Users\Дмитрий\PycharmProjects\pyplanet\functional.py", line 17, in <module>
    print(division(1, 0))
          ^^^^^^^^^^^^^^
  File "C:\Users\Дмитрий\PycharmProjects\pyplanet\functional.py", line 3, in division
    answer = x / y
             ~~^~~
ZeroDivisionError: division by zero

Обычно инструкция finally нужна для того, чтобы корректно освободить ресурсы: открытые файлы, дополнительные процессы или окна и т.д.

Блок finally может идти вместо блоков except, тогда исключения просто не будут обработаны, однако код внутри блока finally всё равно будет выполнен.

Полный список встроенных исключений можно узнать в нашем справочнике. Полезно знать, какие исключения могут возникать в каких ситуациях.