W3docs

Python Try...Except

Изучите блоки try, except, else и finally в Python с примерами. Обработка ZeroDivisionError, ValueError, FileNotFoundError и других исключений.

Когда Python встречает ошибку во время выполнения, он генерирует исключение — объект, описывающий, что пошло не так. Без обработки исключение немедленно завершает программу. Инструкция try...except позволяет перехватывать исключения, реагировать на них корректно и продолжать выполнение программы.

В этой главе рассматриваются:

  • Блок try / except — перехват конкретного исключения
  • Получение сведений об исключении с помощью as
  • Несколько клауз except и перехват нескольких исключений одновременно
  • Блок else — код, выполняющийся только при отсутствии исключений
  • Блок finally — код очистки, выполняющийся всегда
  • Распространённые встроенные исключения и когда они возникают
  • Повторное возбуждение исключений
  • Лучшие практики и типичные ошибки

Базовый блок try...except

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

python— editable, runs on the server

Вывод:

Error: division by zero

Python пытается выполнить деление, генерирует ZeroDivisionError, и блок except обрабатывает его. Клауза as e привязывает объект исключения к переменной e, чтобы вы могли проверить или записать сообщение в журнал.

Если блок try выполняется успешно, блок except пропускается полностью.

Перехват исключения определённого типа

Всегда указывайте тип ожидаемого исключения. Перехват именованного типа делает намерение явным и предотвращает случайное сокрытие несвязанных ошибок.

try:
    number = int("abc")
except ValueError as e:
    print(f"Could not convert: {e}")

Вывод:

Could not convert: invalid literal for int() with base 10: 'abc'

int("abc") генерирует ValueError, поскольку "abc" не является допустимым целым числом. Указание ValueError в клаузе except означает, что любое другое неожиданное исключение будет по-прежнему распространяться и проявляться как ошибка, а не замалчиваться.

Несколько клауз except

Один блок try может содержать несколько клауз except — Python проверяет их сверху вниз и выполняет первое совпадение.

def safe_index(items, index):
    try:
        return items[index]
    except IndexError:
        print("Index out of range.")
    except TypeError:
        print("Index must be an integer.")

safe_index([1, 2, 3], 10)   # IndexError
safe_index([1, 2, 3], "a")  # TypeError

Вывод:

Index out of range.
Index must be an integer.

Порядок важен: размещайте более специфичные типы исключений перед более широкими, чтобы конкретный обработчик выполнялся первым.

Перехват нескольких исключений в одной клаузе

Когда два или более исключений требуют одинаковой реакции, сгруппируйте их в кортеж:

try:
    value = int("not-a-number")
except (ValueError, TypeError) as e:
    print(f"Input error: {e}")

Вывод:

Input error: invalid literal for int() with base 10: 'not-a-number'

Блок else

Блок else выполняется только тогда, когда блок try завершается без генерации исключений. Используйте его для кода, который должен выполняться при успехе, но сам не нуждается в защите от ошибок:

try:
    result = 10 / 2
except ZeroDivisionError:
    print("Cannot divide by zero.")
else:
    print(f"Division succeeded. Result: {result}")

Вывод:

Division succeeded. Result: 5.0

Размещение логики успешного пути в else (а не в конце try) предотвращает случайный перехват исключений, которые может генерировать сам код успешного пути.

Блок finally

Блок finally выполняется в любом случае — независимо от того, успешно ли завершился блок try, было ли исключение перехвачено или оно распространяется без обработки. Используйте его для очистки: закрытия файлов, освобождения блокировок или отключения от базы данных.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Error caught.")
finally:
    print("This always runs — cleanup goes here.")

Вывод:

Error caught.
This always runs — cleanup goes here.

Даже если закомментировать блок except, finally всё равно выполнится перед тем, как Python распространит исключение.

Полная структура с первого взгляда

try:
    # code that may raise an exception
except SomeException as e:
    # handle the exception
except (AnotherError, YetAnother):
    # handle either of these
else:
    # runs only when try succeeded
finally:
    # always runs

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

ИсключениеКогда возникает
ZeroDivisionErrorДеление или остаток от деления на ноль
ValueErrorПравильный тип, неверное значение (например, int("abc"))
TypeErrorОперация применена к неправильному типу
IndexErrorИндекс последовательности выходит за пределы диапазона
KeyErrorКлюч словаря не найден
FileNotFoundErrorФайл или каталог не существует
AttributeErrorОбъект не имеет такого атрибута
ImportErrorМодуль не может быть импортирован
NameErrorИмя переменной не определено

Все они наследуются от базового класса Exception. Можно перехватить Exception для обработки любого из них в одной клаузе, но предпочтительнее использовать конкретные типы там, где это возможно.

Обработка отсутствующего файла

try:
    with open("data.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    print("Error: File not found.")

Вывод (когда data.txt не существует):

Error: File not found.

Повторное возбуждение исключения

Иногда нужно записать исключение в журнал или выполнить частичную очистку, но при этом позволить ему распространиться к вызывающему коду. Используйте голый raise внутри блока except:

def process():
    try:
        result = 10 / 0
    except ZeroDivisionError:
        print("Logging error...")
        raise  # re-raises the original ZeroDivisionError

try:
    process()
except ZeroDivisionError as e:
    print(f"Outer handler caught: {e}")

Вывод:

Logging error...
Outer handler caught: division by zero

Голый raise сохраняет исходную трассировку, поэтому отладка не становится сложнее, чем без обработчика.

Перехват базового класса Exception

Можно использовать Exception в качестве обработчика-перехватчика для любой ошибки, не завершающей систему:

try:
    result = 10 / 0
except Exception as e:
    print(f"An error occurred: {type(e).__name__}: {e}")

Вывод:

An error occurred: ZeroDivisionError: division by zero

type(e).__name__ возвращает конкретное имя класса, даже когда перехват выполняется через базовый класс. Это полезно в обработчиках верхнего уровня, где нужно записывать каждую неожиданную ошибку.

Голый except — и почему его следует избегать

Голый except (без типа исключения) перехватывает буквально всё, включая KeyboardInterrupt (Ctrl+C) и SystemExit, что делает программу сложной для прерывания:

# Avoid this pattern
try:
    result = 10 / 0
except:
    print("Some error occurred")

Предпочтительнее использовать except Exception, если нужен широкий перехват, поскольку он всё равно позволяет KeyboardInterrupt и SystemExit распространяться в штатном режиме.

Лучшие практики

  • Будьте конкретны. Перехватывайте наиболее узкий тип исключения, который имеет смысл.
  • Не замалчивайте исключения. Как минимум запишите ошибку в журнал; никогда не оставляйте блок except пустым.
  • Используйте else для кода успешного пути. Это делает блок try как можно меньшим.
  • Используйте finally для очистки. Или, что ещё лучше, используйте инструкцию with — см. Python with Statement.
  • Избегайте голого except. Используйте except Exception, если нужна широкая сеть.
  • Повторно возбуждайте исключение там, где это уместно. Если вы не можете полностью обработать ошибку, позвольте ей распространиться с помощью raise.

Для намеренного возбуждения исключений и создания собственных классов исключений см. Python raise and Custom Exceptions.

Практика

Практика
What is the functionality of 'Try' and 'Except' in Python?
What is the functionality of 'Try' and 'Except' in Python?
Was this page helpful?