💻 Практические примеры

⚡ Минимальный рабочий пример

from abc import ABC, abstractmethod


class PaymentProcessor(ABC):
    @abstractmethod
    def pay(self, amount: float) -> None:
        pass


class PaypalPayment(PaymentProcessor):
    def pay(self, amount: float) -> None:
        print(f"Paid {amount} via PayPal.")


PaymentProcessor()  # TypeError
PaypalPayment().pay(100)

Пример 1. Абстрактный класс сотрудников

from abc import ABC, abstractmethod


class Employee(ABC):
    """Abstract base class for company employees."""

    def __init__(self, name: str) -> None:
        self.name = name

    @abstractmethod
    def work(self) -> None:
        """Describe what the employee does. Must be implemented."""
        pass

    def take_break(self) -> None:
        """Common behavior for all employees."""
        print(f"{self.name} is taking a break.")


class Programmer(Employee):
    def work(self) -> None:
        print(f"{self.name} writes code.")


class Designer(Employee):
    def work(self) -> None:
        print(f"{self.name} creates layouts.")


# employee = Employee("Bob")  # TypeError!
programmer = Programmer("Alice")
programmer.work()
programmer.take_break()

Что происходит:

  1. Employee наследуется от ABC и объявляет абстрактный метод work().
  2. take_break() — обычный метод, доступный всем наследникам.
  3. Programmer и Designer обязаны реализовать work().

Пример 2. Пользовательское исключение с данными

class AccessDeniedError(Exception):
    """Raised when the user does not meet age requirements."""

    def __init__(self, age: int, min_age: int = 18) -> None:
        self.age = age
        self.min_age = min_age
        super().__init__(f"Access denied: age {age} is below minimum {min_age}")


def check_age(age: int) -> None:
    if age < 18:
        raise AccessDeniedError(age)
    print("Access granted")


try:
    check_age(16)
except AccessDeniedError as e:
    print("Error:", e)
    print("Age:", e.age)
    print("Minimum age:", e.min_age)

Пример 3. Исключение на основе ValueError

class TemperatureTooLowError(ValueError):
    """Raised when temperature is below absolute zero."""
    pass


def set_temperature(value: float) -> None:
    if value < -273.15:
        raise TemperatureTooLowError("Temperature cannot be below absolute zero")
    print(f"Temperature set to {value}°C")


for temp in (15, -300):
    try:
        set_temperature(temp)
    except TemperatureTooLowError as e:
        print("Caught:", e)
    except ValueError as e:
        # Этот блок тоже сработал бы, потому что TemperatureTooLowError — наследник ValueError
        print("Caught as ValueError:", e)

Пример 4. Платёжная система с валидацией

from abc import ABC, abstractmethod


class InvalidPaymentError(Exception):
    """Raised when payment amount is not positive."""
    pass


class PaymentProcessor(ABC):
    """Abstract base class for payment processors."""

    @abstractmethod
    def pay(self, amount: float) -> None:
        """Process a payment of the given amount."""
        pass

    @staticmethod
    def _validate_amount(amount: float) -> None:
        """Guard clause: ensure amount is positive."""
        if amount <= 0:
            raise InvalidPaymentError("Payment amount must be positive.")


class PaypalPayment(PaymentProcessor):
    def pay(self, amount: float) -> None:
        self._validate_amount(amount)
        print(f"Paid {amount} via PayPal.")


class CreditCardPayment(PaymentProcessor):
    def pay(self, amount: float) -> None:
        self._validate_amount(amount)
        print(f"Paid {amount} via Credit Card.")


payments = [PaypalPayment(), CreditCardPayment()]
for payment in payments:
    try:
        payment.pay(100)
        payment.pay(-50)
    except InvalidPaymentError as e:
        print("Error:", e)

Почему так: валидация вынесена в статический метод родителя, чтобы не дублировать код в каждом наследнике.

Пример 5. Итератор с нарастающей суммой

class CumulativeSum:
    """Iterator yielding running totals."""

    def __init__(self, numbers: list[int]) -> None:
        self.numbers = numbers
        self.index = 0
        self.total = 0

    def __iter__(self):
        return self

    def __next__(self) -> int:
        if self.index >= len(self.numbers):
            raise StopIteration
        self.total += self.numbers[self.index]
        self.index += 1
        return self.total


data = [3, 5, 2, 4]
for value in CumulativeSum(data):
    print(value)  # 3 8 10 14

Как запустить в VS Code

  1. Создайте файл example.py в рабочей папке.
  2. Скопируйте код из примера выше.
  3. Откройте терминал в VS Code (Ctrl + `).
  4. Убедитесь, что активировано виртуальное окружение: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  5. Запустите: python example.py