Вопрос с собеседования · Python

Что такое цепочки исключений (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) — несколько ошибок сразу

Подробно

Зачем цепочки. Оборачивая низкоуровневую ошибку в доменную, важно не потерять причину: в логе будут обе трассировки. Без 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.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗raise ... from err заменяет исходное исключение новым, а свои исключения лучше наследовать от BaseException
  • ✗Голый except: — хорошая практика для надёжности
  • ✗Свои исключения нельзя наследовать друг от друга
  • ✓raise ... from err сохраняет причину в __cause__; свои исключения — иерархия от базового класса проекта

Разбор темы

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

Партнёр платформы

Разбор собеседования с sotto

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

Разбор собеседования