Что представляет собой иерархия исключений в Python

Ответ

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

Структура иерархии

Ключевые классы в иерархии организованы следующим образом:

BaseException
 ├── SystemExit
 ├── KeyboardInterrupt
 └── Exception
      ├── ArithmeticError
      │    ├── ZeroDivisionError
      │    └── OverflowError
      ├── AssertionError
      ├── AttributeError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      └── ValueError

Ключевые принципы

  1. Наследование: Все исключения являются потомками BaseException.
  2. Перехват: Блок except перехватывает как указанный класс исключения, так и всех его потомков. Это позволяет обрабатывать группы ошибок (например, except LookupError: ловит и IndexError, и KeyError).
  3. Пользовательские исключения: Рекомендуется создавать свои классы исключений, наследуя их от Exception или его потомков, но не от BaseException.
  4. BaseException: Перехват BaseException — плохая практика, так как он включает системные сигналы выхода (SystemExit, KeyboardInterrupt), которые обычно не должны подавляться.

Пример обработки

Порядок блоков except важен: от более конкретного к более общему.

data = {'key': 'value'}
try:
    # Попытка доступа по несуществующему ключу
    print(data['missing_key'])
except KeyError as e:
    # Этот блок сработает, так как KeyError - более специфичное исключение
    print(f"Ошибка доступа к ключу: {e}")
except LookupError as e:
    # Этот блок не сработает, так как KeyError (потомок LookupError) уже был перехвачен
    print(f"Общая ошибка поиска: {e}")

Ответ 18+ 🔞

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

Вот смотри, как это устроено, блядь:

BaseException (Это типа царь-батюшка всех косяков)
 ├── SystemExit (Это когда скрипт вежливо просится на выход)
 ├── KeyboardInterrupt (А это когда ты в ярости жмёшь Ctrl+C)
 └── Exception (А вот это уже наша, родная, рабочая банда ошибок!)
      ├── ArithmeticError (Туплю с математикой)
      │    ├── ZeroDivisionError (Пытаюсь поделить на ноль, как дебил)
      │    └── OverflowError (Переполнил, блядь, всё, что можно)
      ├── AssertionError (А вот это я сам себя обманул, говоря 'я уверен!')
      ├── AttributeError (Полез к объекту за свойством, которого у него нет)
      ├── LookupError (Ищу что-то не там, где надо)
      │    ├── IndexError (Полез в список за сотым элементом, а там три)
      │    └── KeyError (Полез в словарь за ключом, а его там, блядь, и не пахло)
      └── ValueError (Подсунул функции какую-то дичь вместо нормального значения)

И вся фишка в чём, блядь? В том, что когда ты пишешь except, ты ловишь не только ту самую ошибку, но и всех её детишек! Это как крикнуть "Петров, ко мне!" — и прибежит не только Петров, но и все его младшие братья-недоучки. Поэтому если поймал LookupError, то и IndexError, и KeyError уже в кармане, ёпта!

Но главное правило, блядь, которое мозги не выносит: НЕ ЛОВИ BaseException! Это пиздец какой-то! Ты ж тогда и KeyboardInterrupt поймаешь, и пользователь уже не сможет твой зависший скрипт остановить, будет сидеть и материться, пока батарейка не сядет. Лови всегда от Exception и ниже, понял? И свои косяки создавай, наследуя от них же, а не от царя-батюшки.

А вот смотри, как это на практике выглядит, чтобы не быть, прости господи, распиздяем:

data = {'key': 'value'} # Словарик, в нём одна парочка
try:
    # А я возьму и попробую вытащить то, чего нет! Гений, блядь!
    print(data['missing_key'])
except KeyError as e:
    # Опа! Словил конкретно KeyError! Он самый первый в очереди на поимку.
    print(f"Ошибка доступа к ключу: {e}") # Скажу себе, что я идиот.
except LookupError as e:
    # А этот блок уже проспал всё веселье. KeyError — его сын, его уже поймали раньше.
    # Сюда мы не попадём, даже не мечтай.
    print(f"Общая ошибка поиска: {e}")

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