Способы обработки множественных ошибок в Python

Раздел: Основы Python -> Исключения

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

При написании программ на Python часто возникает необходимость обработать разные типы ошибок. Каждый способ имеет свои цели и случаи применения. Рассмотрим основные подходы.

Как эффективно перехватить несколько исключений в одном блоке?

Наиболее лаконичный и производительный метод - перечисление типов исключений в кортеже. Это позволяет выполнить одинаковое действие для разных ошибок.

try:
    risky_operation()
except (TypeError, ValueError) as e:
    print(f"Ошибка: {e}")

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

Код перехватывает и TypeError, и ValueError. Переменная e содержит объект исключения.

Типичная ошибка: если указать исключения через запятую без скобок (except TypeError, ValueError:), Python 3 воспримет это как синтаксическую ошибку. Правильно - кортеж в круглых скобках.

Куда это применять: когда нужно единообразно реагировать на несколько типов ошибок, например, при парсинге данных.

Как обработать разные исключения разными способами?

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

try:
    value = int(input())
    result = 10 / value
except ValueError:
    print("Введено не число")
except ZeroDivisionError:
    print("Деление на ноль")

Try error python (ошибка try-except в python)

В этом примере ошибки ввода и математическая ошибка обрабатываются по-разному.

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

Цели: логирование разных действий, разные сообщения пользователю.

Как игнорировать определённые исключения?

Модуль contextlib предоставляет контекстный менеджер suppress, который подавляет перечисленные исключения.

from contextlib import suppress

with suppress(FileNotFoundError):
    open("missing.txt")

Файл не найден, но ошибка не всплывает - выполнение продолжается.

Когда использовать: когда отсутствие файла или ресурса не критично. Однако злоупотребление может скрыть реальные проблемы.

Как получить информацию об исключении без явного указания типа?

Функция sys.exc_info() возвращает кортеж (тип, значение, traceback). Используется редко, но бывает полезна в обёртках.

import sys

try:
    1 / 0
except:
    exc_type, exc_value, _ = sys.exc_info()
    print(f"Тип: {exc_type.__name__}, описание: {exc_value}")
Тип: ZeroDivisionError, описание: division by zero

Минусы: такой голый except: перехватывает все исключения, включая SystemExit и KeyboardInterrupt, что нежелательно. Лучше использовать except Exception:.

Ошибка: забыть обработать traceback - занимает память. Если не нужен, используйте подчёркивание.

Как обработать все возможные исключения сразу?

Блок except Exception: перехватывает все встроенные исключения, кроме системных. Это крайняя мера - лучше явно перечислять ожидаемые.

try:
    risky_code()
except Exception as e:
    print(f"Произошла ошибка: {e}")

Проблема: теряется информация о типе ошибки, сложнее отладка. Такой подход оправдан только на верхнем уровне приложения для логирования.

Практические примеры обработки множественных исключений

Ниже приведены расширенные сценарии с подробными пояснениями и выводом.

Пример 1: Раздельная обработка с выполнением разных действий

В одном блоке try можно совмещать кортеж и отдельные исключения, если нужна общая логика для нескольких типов и отдельная - для одного.

Пример
def process_number(n):
    try:
        return 100 / n
    except (TypeError, ValueError) as e:
        return f"Некорректный тип: {e}"
    except ZeroDivisionError:
        return "Деление на ноль"

print(process_number("abc"))
print(process_number(0))
print(process_number(5))
Некорректный тип: unsupported operand type(s) for /: 'int' and 'str'
Деление на ноль
20.0

Обратите внимание: сначала обрабатываются исключения из кортежа (TypeError, ValueError), затем ZeroDivisionError. Если поменять порядок, ошибки типов всё равно будут перехвачены кортежем, но ZeroDivisionError не сможет быть обработан отдельно.

Пример 2: Цепочка исключений (raise from) и её перехват

При повторном возбуждении исключения можно сохранить контекст с помощью raise ... from .... Это помогает трассировать первопричину.

Пример
def open_and_parse(filename):
    try:
        with open(filename) as f:
            return int(f.read())
    except FileNotFoundError as e:
        raise ValueError("Не удалось прочитать данные") from e

try:
    open_and_parse("missing.txt")
except ValueError as e:
    print(f"Поймано: {e}")
    print(f"Причина: {e.__cause__}")
Поймано: Не удалось прочитать данные
Причина: [Errno 2] No such file or directory: 'missing.txt'

Здесь перехватывается только ValueError, но через __cause__ доступна исходная ошибка. Это мощный механизм для построения иерархии ошибок.

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

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

Пример
class DataError(Exception):
    pass

class InvalidFormatError(DataError):
    pass

class MissingFieldError(DataError):
    pass

def validate(data):
    if not isinstance(data, dict):
        raise InvalidFormatError("Ожидается словарь")
    if "name" not in data:
        raise MissingFieldError("Отсутствует поле name")

try:
    validate([1,2])
except DataError as e:
    print(f"Проблема с данными: {e}")
Проблема с данными: Ожидается словарь

Используя базовый класс DataError, перехватываются все ошибки валидации. При этом детализация сохраняется в объекте.

Пример 4: Использование contextlib.suppress в цикле

Если требуется игнорировать исключения для нескольких операций, удобно применить suppress в цикле.

Пример
from contextlib import suppress

files = ["a.txt", "b.txt", "c.txt"]
contents = []
for f in files:
    with suppress(FileNotFoundError, PermissionError):
        with open(f) as fp:
            contents.append(fp.read())
print(f"Успешно прочитано {len(contents)} файлов")
Успешно прочитано 0 файлов

В данном случае все файлы отсутствуют, исключения подавлены, программа продолжает работу. Это удобно для пакетной обработки, где отдельные сбои не критичны.

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

En
несколько исключений python (python)