Как правильно обрабатывать исключения в Python: 5 советов для надежного кода

26 июля 2025 г.

Вступление

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

Пересказ Reddit поста

Недавно я нашел в нашем производственном коде вот такую конструкцию:


except Exception as e:
    logger.error(json.dumps({"reason":"что-то неожиданное случилось", "exception":str(e)}))
    return False

Этот код находится в AWS Lambda, который выполняет роль авторизатора в API Gateway. Если бы я позволил Lambda просто упасть, это бы привело к автоматическому отклонению запроса, что и является желаемым поведением.

Теперь ошибка скрыта, и мне приходится изменять и пересобирать код, чтобы получить больше информации и понять, что произошло. И зачем? Какая польза в ловле этого исключения? Никакой. Просто логируется ошибка, что что-то неожиданное случилось. Великолепно.

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

Перестаньте так делать. Пусть ваша программа падает.

Суть проблемы и хакерский подход

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

Хакеры знают, что исключения — это сигналы о том, что что-то пошло не так. Вместо того чтобы игнорировать их, нужно анализировать и исправлять проблемы, которые они указывают.

Детальный разбор проблемы

Рассмотрим несколько аспектов проблемы:

  • Скрытие ошибок: Ловля исключения и возврат значения без дальнейших действий скрывает истинную причину проблемы.
  • Усложнение отладки: Если ошибка залоггирована и не видно полной трассы стека, это усложняет отладку и поиск причины сбоя.
  • Неправильная обработка: Ловля всех исключений без фильтрации может привести к пропуску критически важных ошибок.

Практические примеры и кейсы

Рассмотрим два примера кода, которые демонстрируют правильные и неправильные подходы к обработке исключений.


# Неправильный подход
try:
    # Код, который может вызвать исключение
    result = 10 / 0
except Exception as e:
    logger.error("Ошибка: %s", str(e))
    return False

В этом примере исключение делит по нулю скрыто, и мы не можем понять, что именно пошло не так.


# Правильный подход
try:
    # Код, который может вызвать исключение
    result = 10 / 0
except ZeroDivisionError as e:
    logger.error("Ошибка: %s", str(e))
    raise

Здесь мы ловим конкретное исключение и позволяем ему распространяться дальше, что позволяет более точно понять, что именно пошло не так.

Экспертные мнения из комментариев

Рассмотрим ключевые мнения из комментариев:

Stop burying your exceptions if you can't actually do anything about them, instead, let them bubble up the stack.

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

Sometimes it is the right decision, sometimes it isn’t.

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

Возможные решения и рекомендации

Вот несколько рекомендаций по обработке исключений:

  • Логируй и перебрасывай: Логируй исключение и позволяй ему распространяться дальше.
  • Лови конкретные исключения: Лови только те исключения, которые ты можешь обработать.
  • Добавь контекст: Добавь дополнительную информацию в исключения, чтобы облегчить отладку.

Заключение с прогнозом развития

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

Исключения — это сигналы. Слушайте их, и ваш код будет жить долго и счастливо.

Практический пример

Рассмотрим пример кода, который демонстрирует правильную обработку исключений. В этом примере мы ловим конкретное исключение, логируем его и перебрасываем дальше.


import logging

# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.ERROR)

def divide_numbers(a: int, b: int) -> float:
    """
    Делит два числа и возвращает результат.

    Args:
        a (int): Числитель
        b (int): Знаменатель

    Returns:
        float: Результат деления
    """
    try:
        result = a / b
    except ZeroDivisionError as e:
        logger.error("Ошибка деления на ноль: %s", str(e))
        raise
    except Exception as e:
        logger.error("Неожиданная ошибка: %s", str(e))
        raise
    return result

# Пример использования функции
try:
    result = divide_numbers(10, 0)
    print(f"Результат: {result}")
except Exception as e:
    print("Ошибка в основном коде:", str(e))

Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE