Иерархия встроенных исключений, несколько except и пользовательские исключения
Тема дорожной карты · Python Programming
Исключения в Python организованы по иерархии, где BaseException является корнем всех исключений, а Exception — корнем обычных прикладных ошибок. Это позволяет разработчикам точно контролировать, какие исключения они хотят перехватить и обработать. Ловить исключения следует сначала по самым конкретным классам, чтобы обеспечить точную логику обработки. Например, кортеж except (TypeError, ValueError): позволяет одновременно перехватить несколько типов исключений.
Для создания пользовательских исключений рекомендуется наследовать их от Exception, а не от BaseException, так как последний включает исключения, которые обычно не перехватываются, такие как KeyboardInterrupt и SystemExit. Это позволяет группировать пользовательские исключения под общим базовым классом, таким как MyAppError, что облегчает их перехват вызывающим кодом.
Добавление структурных атрибутов, таких как code и request_id, вместо форматирования состояния в сообщение исключения, делает логи и тесты более чистыми и структурированными. Это особенно полезно при работе с большими системами, где требуется точное отслеживание и диагностика ошибок.
Как это работает
Иерархия встроенных исключений, несколько except и пользовательские исключения используют блоки try/except/else/finally для обработки рантайм-ошибок. Иерархия исключений начинается с BaseException, который является родительским классом для всех исключений, а Exception — корнем обычных прикладных ошибок. Стандартные подклассы, такие как ValueError, TypeError, KeyError и IOError, наследуются от Exception.
Пользовательские исключения должны наследоваться от Exception и могут содержать дополнительные атрибуты. Команда raise позволяет пробрасывать исключение, а raise ... from cause сохраняет цепочку причин, что полезно для диагностики ошибок. Контекстные менеджеры, используемые в конструкции with, гарантируют cleanup (закрытие файла, отпускание lock) даже при исключении, что важно для обеспечения корректной работы приложения.
Модуль logging является стандартным инструментом для записи ошибок с отметками времени, уровнями и обработчиками. Это позволяет структурировано собирать и анализировать информацию о работе приложения.
Когда применять
Исключения следует ловить только в тех случаях, когда реально можно восстановиться после ошибки. Никогда не используйте except: без аргумента, так как это может привести к тихому съеданию исключений, что затрудняет отладку. Исключения предназначены для обработки исключительных условий, а не для управления потоком выполнения. Например, проверка наличия ключа в словаре через if key in d предпочтительнее, чем попытка доступа к ключу через try: d[key] except KeyError.
Создание пользовательских исключений полезно, когда вызывающий код должен отличить ошибки вашего приложения от общих ошибок. Это позволяет более точно контролировать поведение программы и обеспечивает лучшую отладку. Использование logging.exception() внутри блока except позволяет автоматически захватывать traceback, что упрощает процесс диагностики ошибок.
Типичные ошибки
Типичные ошибки при работе с исключениями включают тихое съедание исключений (except: pass), ловлю Exception на верхнем уровне функции и перебрасывание исключения без контекста (from None), что может привести к потере информации о первоначальной ошибке. Также распространенной ошибкой является использование assert для рантайм-проверок, так как команда python -O удаляет все утверждения, что может привести к непредсказуемому поведению программы. Логирование через print вместо использования модуля logging также является распространенной ошибкой, так как это не позволяет использовать уровни логирования, ротацию файлов и структурированные данные.
Traceback — это ключевой источник информации при отладке ошибок. Чтобы найти место вызова, следует читать traceback сверху вниз, а чтобы определить реальное место ошибки, следует читать снизу вверх.