Перейти к содержанию

Python. Обработка ошибок

Исключительная ситуация (exception) - это особый вид объектов Python, которые сообщают о возникновении ошибок в коде.

Обработка исключений

При возникновении ошибки, объект исключительной ситуации начинает передаваться вверх по списку вызова функций, приведших к участку кода с ошибкой. Для того, чтобы узнать и как-то отреагировать на возникшую ошибку, в языке Python есть специальный составной оператор try-except, который позволяет перехватить исключение.

Объявление перехватчика исключений выглядит следующим образом:

try:
    <Код, который может вызвать ошибку>
except:
    <Код выполнится, если возникло исключение>
else:
    <Код выполнится в случае, если исключения не было>
finally:
    <Код выполнится в любом случае>

Секции else и finally являются необязательными. При записи оператора их можно не указывать.

Рассмотрим пример с функцией, которая выполняет деление двух чисел.

def delenie(a, b):
    return a / b

print(delenie(1, 0))  # Будет ошибка ZeroDivisionError: division by zero

Теперь решим, что ошибка нам не нужна. Просто будем возвращать 0. Заключим строки кода, которые могут вызвать ошибку в try-except

def delenie(a, b):
    try:
        result = a / b
    except:
        result = 0
    return result

print(delenie(1, 0))  # Напечатает 0

У такой реализации функции delenie() есть одна особенность. Значение 0 будет возвращено для любой ошибки, которая может возникнуть в процессе деления. Например, попробуем разделить число на строку

print(delenie(1, 'строка'))  # Напечатает 0

Теперь, сделаем так, чтобы значение 0 возвращалось только для ошибок деления на 0, а остальные ошибки оставались необработанными. Для этого объявление секции except может быть расширено следующим образом

try:
    <Код, который может вызвать ошибку>
except <ТипИсключения> as e:
    <Код выполнится, если возникло исключение ТипИсключения>

В нашем случае, в качестве <ТипИсключения> нужно указать ZeroDivisionError.

def delenie(a, b):
    try:
        result = a / b
    except ZeroDivisionError as e:
        result = 0
    return result

print(delenie(1, 0))  # Напечатает 0
print(delenie(1, 'строка'))  # Ошибка TypeError: unsupported operand type(s) for /: 'int' and 'str'

Исключения в Python выстроены в отношения тип-подтип. При указании типа исключения в except, в этом блоке будет обработано исключение не только указанного типа, но и всех его подтипов.

Например, согласно документации, ZeroDivisionError является подтипом более общего типа ArithmeticError, который в свою очередь содержит ещё такие подтипы как OverflowError и FloatingPointError. При указании except ArithmeticError as e:, в указанный блок попадут исключения всех трех его подтипов.

Самым базовым типом исключения является Exception. Если указать его в except, то в эту секцию попадут вообще все возникшие исключения. Таким образом, следующие два обработчика исключений эквивалентны:

try:
    ...
except Exception as e:
    ...

# эквивалентно записи без указания типа исключения

try:
    ...
except:
    ...

Можно указывать несколько секций except. Если мы хотим обрабатывать ZeroDivisionError и OverflowError отдельно, то можем записать обработчик так

try:
    ...
except ZeroDivisionError as e:
    ...
except OverflowError as e:
    ...

Например, если в нашей функции delenie() мы захотим, чтобы при делении на 0 возвращалось 0, а при несовместимости типов (делении на строку), возвращалось None, то можем записать так

def delenie(a, b):
    try:
        result = a / b
    except ZeroDivisionError as e:
        result = 0
    except TypeError as e:
        result = None
    return result

print(delenie(1, 0))  # Напечатает 0
print(delenie(1, 'строка'))  # Напечатает None

Выбрасывание исключений

Можно не только обрабатывать исключительные ситуации, но и генерировать свои. Для этого служит оператор raise. Синтаксис записи оператора:

raise <ТипИсключения>(<Параметры)

В качестве <ТипИсключения> можно использовать любой из существующих в Python типов. Если не знаете, какой лучше, то просто используйте Exception.

В <Параметры> обычно указывается просто строка с описанием ошибки.

def delenie(a, b):
    if b == 0:
        raise Exception('А вот не надо делить на 0')
    return a / b

При вызове оператора raise выполнение текущей функции останавливается, и исключение передается в вызвавшую её функцию. Если там нет блока try-except, то выполнение вызвавшей функции так же останавливается и исключение передается ещё выше по стеку вызова и так далее.

Если try-except нигде в коде программы так и не встретится, то вы увидите сообщение об ошибке в консоли. Такие исключения ещё называются необработанными.