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

Что такое контракт при взаимодействии модулей в Python?

1.7 Middle🔥 111 комментариев
#Другое

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

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

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

Что такое контракт при взаимодействии модулей в Python?

Контракт модуля — это неявное или явное соглашение между частями программы о том, как они должны взаимодействовать. Контракт определяет ожидания обеих сторон: что одна сторона должна предоставить, а другая обещает правильно использовать. Это концепция из дизайна по контракту (Design by Contract).

Элементы контракта

  1. Предусловие (Precondition) — что должно быть истинно перед вызовом
  2. Постусловие (Postcondition) — что гарантированно будет истинно после вызова
  3. Инвариант класса (Class Invariant) — что должно оставаться истинным всегда

Простой пример контракта

class BankAccount:
    def __init__(self, balance: float):
        # Инвариант: баланс всегда >= 0
        if balance < 0:
            raise ValueError("Баланс не может быть отрицательным")
        self._balance = balance
    
    def withdraw(self, amount: float) -> float:
        # Предусловие: сумма положительна и <= баланса
        if amount <= 0:
            raise ValueError("Сумма должна быть положительной")
        if amount > self._balance:
            raise ValueError("Недостаточно средств")
        
        # Выполняем операцию
        self._balance -= amount
        
        # Постусловие: баланс уменьшился на переданную сумму
        return amount
    
    def deposit(self, amount: float):
        # Предусловие
        if amount <= 0:
            raise ValueError("Сумма должна быть положительной")
        
        old_balance = self._balance
        self._balance += amount
        
        # Постусловие: баланс увеличился
        assert self._balance == old_balance + amount

Контракт при использовании типов (Type Hints)

Типизация — это форма контракта: функция обещает возвращать определённый тип.

from typing import List, Optional

def find_user(user_id: int) -> Optional[dict]:
    """
    Контракт:
    - Принимает целое число user_id
    - Возвращает словарь с данными или None
    
    Предусловие: user_id > 0
    Постусловие: результат либо dict, либо None
    """
    if user_id <= 0:
        raise ValueError("ID должен быть положительным")
    # ...
    return {"id": user_id, "name": "John"}  # или None

Явный контракт через документацию

class DataValidator:
    def validate_email(self, email: str) -> bool:
        """
        Проверяет корректность email.
        
        Контракт:
        - Принимает строку (не None)
        - Возвращает True если email валидный, False иначе
        - Не выбрасывает исключения для некорректного формата
        - Работает с email от 5 до 254 символов
        
        Args:
            email: строка с email адресом
        
        Returns:
            True если email валидный
        
        Raises:
            TypeError: если email не строка
        """
        if not isinstance(email, str):
            raise TypeError("Email должен быть строкой")
        
        if len(email) < 5 or len(email) > 254:
            return False
        
        return "@" in email and "." in email

Контракт между модулями

Когда модуль A использует модуль B:

# module_b.py
class PaymentProcessor:
    def process_payment(self, amount: float, card: dict) -> bool:
        """
        Контракт модуля B:
        
        Предусловие (требования к модулю A):
        - amount > 0
        - card содержит ключи: number, cvv, expiry
        - card[number] — 16 цифр
        
        Постусловие (гарантии модуля B):
        - Возвращает True если платёж успешен
        - Никогда не выбросит исключение для валидного ввода
        - Логирует все попытки платежа
        """
        # Проверяем предусловия
        if amount <= 0:
            raise ValueError("Сумма должна быть положительной")
        
        required_keys = {"number", "cvv", "expiry"}
        if not required_keys.issubset(card.keys()):
            raise ValueError(f"Карта должна содержать: {required_keys}")
        
        # Обработка платежа
        result = self._send_to_gateway(amount, card)
        return result

# module_a.py (использует module_b)
class OrderService:
    def __init__(self, payment_processor: PaymentProcessor):
        self.payment_processor = payment_processor
    
    def complete_order(self, order: dict) -> bool:
        """
        Пример контракта использования:
        - Вызывает payment_processor только с валидными данными
        - Полагается на то, что процессор гарантирует безопасность
        - Не ловит исключения от процессора (они — признак нарушения контракта)
        """
        amount = order["total"]
        card = order["payment_card"]
        
        # Модуль A соблюдает контракт: передаёт валидные данные
        if amount <= 0 or not card.get("number"):
            raise ValueError("Невалидный заказ")  # Проверяем перед вызовом
        
        # Теперь можем безопасно вызвать payment_processor
        success = self.payment_processor.process_payment(amount, card)
        return success

Проверка контракта (Assertions)

def divide(a: int, b: int) -> float:
    # Предусловие: b не должно быть 0
    assert b != 0, "Делитель не может быть нулём"
    
    result = a / b
    
    # Постусловие: результат корректен
    assert abs(result * b - a) < 0.0001, "Ошибка расчёта"
    
    return result

Нарушение контракта

# Нарушение контракта — модуль A не соблюдает предусловия
processor = PaymentProcessor()
processor.process_payment(-100, {})  # Предусловие нарушено!

# Соблюдение контракта — модуль A проверяет данные
if order["amount"] > 0 and valid_card(order["card"]):
    processor.process_payment(order["amount"], order["card"])

Зачем нужны контракты?

  1. Ясность — четко определены ожидания
  2. Надёжность — каждый модуль знает границы ответственности
  3. Отладка — легче найти, кто нарушил контракт
  4. Тестирование — проверяем соблюдение контракта
  5. Рефакторинг — контракт остаётся, реализация может меняться

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