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

Большинство ошибок времени выполнения в Python проявляются как исключения – именованный, структурированный способ сообщить, что что-то пошло не так. ValueError, TypeError, KeyError, OSError, MemoryError – всё это примеры; каждое из них является классом, и возбуждение исключения останавливает текущий вызов и ищет обработчик в окружающем коде.

2.25.1. try / except

Оберните блок кода в try, чтобы перехватить любое возбуждаемое им исключение:

try:
    n = int(input_text)
except ValueError:
    n = 0

Если преобразование int(...) завершается неудачей, управление переходит к блоку except вместо дальнейшего распространения ошибки. Если input_text был корректной строкой с целым числом, блок except пропускается.

Один try может иметь несколько блоков except, каждый из которых перехватывает свой вид ошибки:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python сопоставляет их по порядку; первый, чей класс исключения подходит, обрабатывает проблему. Перехват Exception (базового класса почти для всего) обрабатывает любую ошибку; оставьте это для самого внешнего уровня программы, где альтернатива – аварийное завершение.

Предупреждение

Пустой except: (без класса после ключевого слова) также перехватывает KeyboardInterrupt – исключение, которое IDE отправляет, когда вы нажимаете кнопку stop, чтобы прервать выполняющийся скрипт. Цикл, обёрнутый в пустой except: pass, проглотит прерывание и продолжит работу, не оставив никакого способа остановить скрипт, кроме перезагрузки питания.

Предпочитайте except Exception: пустому except:, когда вам действительно нужно перехватывать широко. KeyboardInterrupt наследуется от BaseException, а не от Exception, поэтому except Exception: оставляет кнопку остановки рабочей.

2.25.1.1. Исследование исключения

Чтобы прочитать сообщение, прикреплённое к исключению, назовите его с помощью as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

Переменная, связанная через as, действительна только внутри блока except.

2.25.2. else и finally

У блока try есть два необязательных дополнения.

else выполняется только тогда, когда try завершился без возбуждения исключения:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

Помещение того, «что делать, когда сработало», в else сохраняет блок try узким – в try должна попадать только строка, которая может завершиться неудачей.

finally выполняется в конце в любом случае – независимо от того, удался ли try, возбудил исключение, которое было обработано, или возбудил исключение, которое вот-вот распространится дальше:

try:
    do_work()
finally:
    cleanup()
Диаграмма потока, показывающая четыре возможных пути через try / except / else / finally: успех идёт в else, затем в finally; перехваченное исключение идёт в except, затем в finally; неперехваченное исключение идёт в finally, затем распространяется дальше.

finally выполняется всегда. else выполняется только на пути без исключения.

Для большинства шаблонов захвата/освобождения предпочитайте менеджер контекста паре try / finally – сам ресурс управляет своей очисткой.

2.25.3. Распространённые встроенные исключения

Краткий список исключений, с которыми вы будете часто сталкиваться:

  • ValueError – правильный тип, неправильное значение (bytes([300]) – 300 имеет правильный тип, но выходит за допустимый диапазон байта 0..255).

  • TypeError – совершенно неправильный тип (len(42)).

  • KeyError – отсутствующий ключ в dict.

  • IndexError – индекс за пределами конца последовательности.

  • AttributeError – обращение к атрибуту, который не существует ("abc".foo).

  • OSError – сбой файловой системы или ввода-вывода.

  • MemoryError – закончилась куча. На среде выполнения с ограниченной памятью это может происходить во время нормальной работы – а не только в патологических случаях.