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

Общая информация

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

Иерархия исключений

Полный список исключений можно найти по ссылке: Иерархия исключений в python.

Основные конструкции для обработки исключений

Блок try

Код в блоке try python попытается выполнить, а если не получится - выполнится блок except.

Блок except

Не используйте bare except (голый, без указания конкретных исключений). Примеры правильного применения:

try:
    <code>
except IndexError:
    <code>

Применение as:

try:
    <code>
except IndexError as exc:
    print(f"Exception: {exc}")

Multiple exceptions:

try:
    <code>
except (IndexError, ImportError, NameError) as exc:
    print(f"Exception: {exc}")

Блок else

Код в else выполнится, если не был вызван блок except, то есть не отловились исключения, и не было ошибок:

try:
    <code>
except (IndexError, ImportError, NameError) as exc:
    print(f"Exception: {exc}")
else:
    print("Код был успешно выполнен")

Блок finally

Код в finally выполнится в любом случае. Например:

try:
    tmp_file = open("file.txt", "w")
    input_numbers = int(input("Enter the numbers: "))
except (IsADirectoryError, PermissionError, ValueError) as exc:
    print(f"Exception: {exc}")
else:
    print(f"Строка {input_numbers} успешно записана в файл {tmp_file.name}")
finally:
    tmp_file.close()

Таким образом, файл закроется в любом случае. Вариант без finally не гарантирует закрытие файла, так как программа может завершиться из-за неопределенных в except исключений.

Использование raise

Вместо самодельного прерывания, используйте raise, который предназначен для этой задачи:

line_count = 0
for line in text:
    length = len(line)
    if length < 3:
        line_count += 1
        raise BaseException(f"Длина {line_count} строки меньше 3 символов")

Проброс исключения

Пробросом исключения называют передачу информации об ошибке дальше по коду, не обрабатывая её в текущем месте. Делается с помощью raise.

Пример функции divide(), которая пробрасывает свое исключение, чтобы передать вызывающему коду информацию об ошибке:

def divide(a, b):
    if b == 5:
        raise ValueError("Не надо делить на 5 😡")
    return a / b
 
try:
    result = divide(10, 5)
except ValueError as e:
    print(e)

Output:

Не надо делить на 5 😡

Еще один пример проброса исключения:

def read_file(filename):
    try:
        with open(filename, 'r') as f:
            return f.read()
    except FileNotFoundError:
        raise FileNotFoundError(f"Файл {filename} не найден.")
 
try:
    content = read_file("неcуществующий_файл.txt")
except FileNotFoundError as e:
    print(e)

Output:

Файл неcуществующий_файл.txt не найден.

Пример проброса исключения вверх по стеку:

min = 100
try:
    if min > 10:
        raise Exception('min must be less than 10')
except Exception:
    print('Моя ошибка')
    raise

Output:

> Моя ошибка
> Traceback (most recent call last):
>   File "test.py", line 5, in <module>
>     raise Exception('min must be less than 10')
> Exception: min must be less than 10

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

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

>>> short_list = [1, 2, 3]
>>> while True:
        value = input('Position [q to quit]? ')
        if value == 'q':
            break
        try:
            position = int(value)
            print(short_list[position])
        except IndexError as err:
            print('Bad index:', position)
        except Exception as other:
            print('Something else broke:', other)

Output:

Position [q to quit]? 1
2
Position [q to quit]? 3
Bad index: 3
Position [q to quit]? two
Something else broke: invalid literal for int() with base 10: 'two'
Position [q to quit]? q

Создание своих исключений

Для создания своих исключений нужно определить новый тип объекта с помощью класса. Пример:

>>> class UppercaseException(Exception):
        pass
 
>>> words = ['eeenie', 'meenie', 'miny', 'MO']
>>> for word in words:
        if word.isupper():
            raise UppercaseException(word)
 
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
__main__.UppercaseException: MO

Для вывода информации об исключении:

>>> try:
        raise OopsException('panic')
    except OopsException as exc:
        print(exc)
 
panic

Соус: Книга “Простой Python Глава 9. “ФункцииИсключения

python