Иерархия исключений и несколько except

Иерархия исключений Python: BaseException и Exception, несколько блоков except, порядок обработчиков и перехват группы ошибок.

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

Дерево исключений (главные ветви)

BaseException
├── SystemExit          # sys.exit()
├── KeyboardInterrupt   # Ctrl+C
└── Exception           # все «обычные» ошибки
    ├── ValueError
    ├── TypeError
    ├── ArithmeticError
    │   └── ZeroDivisionError
    ├── LookupError
    │   ├── IndexError
    │   └── KeyError
    ├── OSError
    │   └── FileNotFoundError
    └── ...и другие

Перехват родительского класса ловит и все его потомки. Например, except LookupError поймает и IndexError, и KeyError.

Несколько блоков except

def divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("Ошибка: делитель равен нулю")
    except TypeError:
        print("Ошибка: оба аргумента должны быть числами")

print(divide(10, 2))    # 5.0
print(divide(10, 0))    # Ошибка: делитель равен нулю
print(divide(10, "x"))  # Ошибка: оба аргумента должны быть числами

Вывод:

5.0
Ошибка: делитель равен нулю
Ошибка: оба аргумента должны быть числами

Python проверяет блоки except сверху вниз и выполняет только первый подходящий.

Порядок важен: частное раньше общего

# Неправильно: Exception поймает всё первым
try:
    x = int("abc")
except Exception:
    print("Общая ошибка")
except ValueError:
    print("ValueError")   # никогда не выполнится!

# Правильно: сначала конкретный, потом общий
try:
    x = int("abc")
except ValueError:
    print("Не удалось преобразовать строку в число")
except Exception as e:
    print(f"Другая ошибка: {e}")

Вывод:

Не удалось преобразовать строку в число

Перехват нескольких типов сразу

Если несколько исключений обрабатываются одинаково, их объединяют в кортеж:

def get_element(data, key):
    try:
        return data[key]
    except (KeyError, IndexError) as e:
        print(f"Элемент не найден: {e}")
        return None

print(get_element({"a": 1}, "b"))   # Элемент не найден: 'b'
print(get_element([1, 2, 3], 10))   # Элемент не найден: list index out of range

Вывод:

Элемент не найден: 'b'
Элемент не найден: list index out of range

Коротко

  • Все исключения наследуют от BaseException; «обычные» ошибки — от Exception.
  • Несколько блоков except проверяются сверху вниз — выполняется только первый совпавший.
  • Ставьте конкретные исключения перед общими (например, ValueError перед Exception).
  • Несколько типов в одном блоке — через кортеж: except (ValueError, TypeError).
Проверьте себя
1. В каком порядке Python проверяет несколько блоков except?
AВ случайном порядке
BСначала самый общий, потом конкретный
CСверху вниз, выполняет первый подходящий
DСнизу вверх
2. Как в одном except поймать и KeyError, и IndexError?
Aexcept KeyError or IndexError
Bexcept KeyError, IndexError
Cexcept (KeyError, IndexError)
Dexcept LookupError только
3. Какой базовый класс объединяет ValueError, TypeError и большинство «обычных» ошибок?
ABaseException
BException
CError
DRuntimeError
Поддержать проект