Получить доступ

Что такое цепочки исключений (raise from) и как правильно создавать свои исключения?

MiddleИсключения и контекстные менеджеры
Ещё спрашивают так:
  • Чем raise ... from ... отличается от raise внутри except?
  • Как спроектировать иерархию исключений в Python-проекте?
Коротко

raise NewError(...) from err сохраняет исходную ошибку в __cause__ — в трассировке «The above exception was the direct cause». Простой raise внутри except ставит __context__ («During handling … another exception occurred»). from None скрывает исходную. Свои исключения — иерархия от одного базового класса проекта.

  • Ловить базовый класс библиотеки — удобно вызывающему
  • Не ловить голый except: — перехватит KeyboardInterrupt и SystemExit
  • add_note (3.11) — добавить контекст к исключению без обёртки

А ещё ExceptionGroup и except* (3.11) — несколько ошибок сразу

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Зачем цепочки. Оборачивая низкоуровневую ошибку в доменную, важно не потерять причину: в логе будут обе трассировки. Без from Python всё равно сохранит контекст, но сообщение будет выглядеть как ошибка в обработчике, а не намеренное преобразование.

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

  • один базовый класс на библиотеку или модуль (PaymentError);
  • подклассы по тому, как вызывающий будет реагировать (повторить, показать пользователю, упасть);
  • данные в атрибутах, а не только в тексте: CardDeclined(code=...).

Правила обработки: ловить узко, логировать один раз, не глотать молча, не использовать except Exception: pass. Голый except: перехватывает BaseException, включая KeyboardInterrupt, SystemExit и отмену asyncio.

ExceptionGroup (3.11) объединяет несколько независимых ошибок (TaskGroup, параллельная валидация); except* ValueError обрабатывает свою часть группы, остальные идут дальше.

Пример

class PaymentError(Exception): ...
class CardDeclined(PaymentError): ...
class ProviderUnavailable(PaymentError): ...

def charge(card):
    try:
        return gateway.charge(card)
    except requests.Timeout as e:
        raise ProviderUnavailable("шлюз не ответил") from e

Что хотят услышать

  • __cause__ и __context__.
  • Проектирование иерархии.
  • Почему нельзя голый except.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем