← Назад к вопросам

В чём разница между exception и BaseException?

2.0 Middle🔥 111 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чём разница между Exception и BaseException

В Python есть два корневых класса для исключений: Exception и BaseException. Различие между ними критично для правильной обработки ошибок.

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

BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
    ├── StopIteration
    ├── ArithmeticError
    ├── LookupError
    ├── AssertionError
    ├── AttributeError
    ├── ImportError
    ├── RuntimeError
    ├── ValueError
    ├── TypeError
    └── ... (ещё 50+ встроенных исключений)

BaseException — корень всех исключений

BaseException — это абсолютный корень иерархии. От него наследуются системные исключения и сигналы:

# Что наследуется напрямую от BaseException:

# 1. SystemExit — выход из программы
try:
    sys.exit(42)
except BaseException as e:
    print(type(e))  # <class 'SystemExit'>
    print(e.code)   # 42

# 2. KeyboardInterrupt — Ctrl+C
try:
    while True:
        pass
except BaseException as e:
    print(type(e))  # <class 'KeyboardInterrupt'>

# 3. GeneratorExit — завершение генератора
def my_gen():
    try:
        yield 1
    except BaseException as e:
        print(type(e))  # <class 'GeneratorExit'>

g = my_gen()
next(g)
g.close()

Exception — исключения приложения

Exception — это базовый класс для всех ошибок приложения, которые нужно обрабатывать:

# Exception ловит ошибки приложения
try:
    x = 1 / 0
except Exception as e:
    print(type(e))  # <class 'ZeroDivisionError'>
    print("Обработано приложением")

# Exception НЕ ловит системные сигналы
try:
    raise KeyboardInterrupt()
except Exception as e:
    print("Это никогда не выполнится")
except KeyboardInterrupt:
    print("KeyboardInterrupt поймана")

Ключевые различия

АспектBaseExceptionException
Наследуется отНичегоBaseException
НазначениеВсе исключения и сигналыОшибки приложения
Какие ловитВсё (SystemExit, KeyboardInterrupt, Exception)Только ошибки приложения
Когда использоватьРедкоВ 99% случаев
Кастомные классыПлохая идея наследоватьсяНормально наследоваться

Практические примеры ошибок

❌ Плохо: ловить BaseException

# Это перехватит даже SystemExit!
try:
    user_code()
except BaseException:
    print("Перехвачено всё")
    # Но SystemExit уже не сработает!

❌ Плохо: ловить голый except

# Deprecated, но ещё используется
try:
    data = json.loads(user_input)
except:  # ловит ВСЕГО! (включая KeyboardInterrupt)
    print("Ошибка")

✅ Хорошо: ловить Exception

try:
    data = json.loads(user_input)
except Exception as e:  # ловит только ошибки приложения
    logger.error(f"JSON error: {e}")
    raise  # пробросить дальше
except KeyboardInterrupt:  # если нужно обработать Ctrl+C отдельно
    print("Прерывание пользователем")

Почему не ловить BaseException

# Сценарий: долгоживущий сервис
import time

def server_loop():
    try:
        while True:
            handle_request()
            time.sleep(1)
    except BaseException:  # ПЛОХО!
        print("Перехвачено")

# Пользователь нажал Ctrl+C, но программа не завершилась!
# SystemExit был перехвачен и проигнорирован

Правильный способ:

def server_loop():
    try:
        while True:
            handle_request()
            time.sleep(1)
    except Exception:
        print("Ошибка приложения")
    except KeyboardInterrupt:
        print("Выход по Ctrl+C")
        sys.exit(0)

Кастомные исключения

# ✅ Правильно: наследоваться от Exception
class ValidationError(Exception):
    pass

class DatabaseError(Exception):
    pass

class APIError(Exception):
    pass

# ❌ Плохо: наследоваться от BaseException
class BadError(BaseException):  # Не делай так!
    pass

# Использование
try:
    validate_user(data)
except ValidationError as e:
    logger.error(f"Validation failed: {e}")
except DatabaseError as e:
    logger.error(f"DB error: {e}")
except Exception as e:
    logger.error(f"Unexpected error: {e}")

Иерархия кастомных исключений

# Базовое исключение приложения
class AppError(Exception):
    pass

# Специфичные ошибки
class UserError(AppError):
    pass

class NotFoundError(UserError):
    pass

class AuthenticationError(UserError):
    pass

class ValidationError(AppError):
    pass

# Использование
try:
    user = find_user(user_id)
except NotFoundError:
    return 404, "User not found"
except ValidationError:
    return 400, "Invalid data"
except AppError as e:
    return 500, f"Internal error: {e}"
except Exception as e:
    logger.error(f"Unexpected: {e}")
    return 500, "Critical error"

Обработка специфичных исключений

import json
import requests

# Хорошая обработка разных типов ошибок
try:
    response = requests.get(url, timeout=5)
    data = response.json()
except requests.Timeout:
    logger.error("Request timeout")
except requests.ConnectionError:
    logger.error("Connection error")
except json.JSONDecodeError:
    logger.error("Invalid JSON response")
except requests.RequestException as e:
    logger.error(f"Request error: {e}")
except Exception as e:
    logger.error(f"Unexpected error: {e}")

Проверка типов исключений

# Проверить, какое точно исключение
try:
    risky_operation()
except Exception as e:
    # Проверить тип
    if isinstance(e, ValueError):
        print("Некорректное значение")
    elif isinstance(e, KeyError):
        print("Ключ не найден")
    else:
        print(f"Неизвестная ошибка: {type(e)}")

# Или через __class__
except Exception as e:
    if e.__class__.__name__ == "ValueError":
        print("Value error")

Повторное выбросение исключения

def log_and_reraise():
    try:
        risky_operation()
    except Exception as e:
        # Залогировать и пробросить дальше
        logger.error(f"Error: {e}", exc_info=True)
        raise  # Пробрасывает ТОЖЕ исключение, не новое!

# Или с контекстом
except Exception as e:
    logger.error(f"Error: {e}")
    raise RuntimeError(f"Failed: {e}") from e  # from e сохраняет оригинальный стек

Лучшие практики

# 1. Ловить специфичные исключения
try:
    data = database.query(sql)
except DatabaseConnectionError:
    # обработать ошибку подключения
    pass
except DatabaseQueryError:
    # обработать ошибку запроса
    pass

# 2. Использовать Exception для неожиданных ошибок
except Exception as e:
    logger.error(f"Unexpected error: {e}")
    raise

# 3. Никогда не ловить BaseException
# ❌ except BaseException:
# ✅ except Exception:

# 4. Специальная обработка для системных сигналов
try:
    main()
except KeyboardInterrupt:
    print("Завершение")
    sys.exit(0)
except SystemExit as e:
    print(f"Выход с кодом {e.code}")

# 5. Документировать выбрасываемые исключения
def divide(a, b):
    """
    Raises:
        ValueError: если b равно нулю
    """
    if b == 0:
        raise ValueError("Деление на нуль")
    return a / b

Итого

Exception vs BaseException:

  • BaseException — абсолютный корень, включает SystemExit и KeyboardInterrupt
  • Exception — базовый класс ошибок приложения, это то, что нужно ловить в 99% случаев
  • Никогда не лови BaseException в приложениях
  • Создавай кастомные исключения наследуясь от Exception
  • Лови специфичные исключения перед общими