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

Что такое классы ООП?

1.2 Junior🔥 211 комментариев
#Python Core

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

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

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

# Классы в объектно-ориентированном программировании

Класс — это blueprint (чертёж) для создания объектов. Это шаблон, который определяет структуру (атрибуты) и поведение (методы) объектов определённого типа. Классы — основа ООП.

Базовая концепция

# Класс — это шаблон
class Dog:
    # Атрибуты класса (shared всеми объектами)
    species = "Canis familiaris"
    
    # Конструктор — инициализирует объект
    def __init__(self, name: str, age: int):
        # Атрибуты экземпляра (уникальны для каждого объекта)
        self.name = name
        self.age = age
    
    # Методы — поведение
    def bark(self) -> str:
        return f"{self.name} says Woof!"
    
    def birthday(self) -> None:
        self.age += 1

# Создание объектов (экземпляров класса)
dog1 = Dog("Rex", 5)
dog2 = Dog("Max", 3)

print(dog1.bark())      # Rex says Woof!
dog1.birthday()
print(dog1.age)         # 6

Четыре столпа ООП

1. Инкапсуляция (Encapsulation)

Хранение данных и методов вместе, скрытие внутренней реализации.

class BankAccount:
    def __init__(self, balance: float):
        # Приватный атрибут (по соглашению _balance)
        self._balance = balance
    
    # Getter
    @property
    def balance(self) -> float:
        return self._balance
    
    # Setter с проверкой
    @balance.setter
    def balance(self, value: float) -> None:
        if value < 0:
            raise ValueError("Balance не может быть отрицательным")
        self._balance = value
    
    def withdraw(self, amount: float) -> None:
        if amount > self._balance:
            raise ValueError("Недостаточно средств")
        self._balance -= amount

# Использование
account = BankAccount(1000)
print(account.balance)   # 1000
account.withdraw(100)
print(account.balance)   # 900
account.balance = -100   # ValueError!

Смысл: пользователь не может напрямую изменить balance, только через контролируемые методы.

2. Наследование (Inheritance)

Дочерний класс наследует свойства и методы родительского класса.

# Родительский класс (базовый)
class Animal:
    def __init__(self, name: str):
        self.name = name
    
    def speak(self) -> str:
        return f"{self.name} makes a sound"

# Дочерние классы наследуют от Animal
class Dog(Animal):
    def speak(self) -> str:
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self) -> str:
        return f"{self.name} says Meow!"

# Использование
dog = Dog("Rex")
cat = Cat("Whiskers")

print(dog.speak())   # Rex says Woof!
print(cat.speak())   # Whiskers says Meow!

# Оба имеют атрибут name от родителя
print(dog.name)      # Rex
print(cat.name)      # Whiskers

super() для вызова родительского класса:

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        super().__init__(name)  # Вызываем __init__ родителя
        self.breed = breed
    
    def speak(self) -> str:
        parent_sound = super().speak()  # Rex makes a sound
        return f"{parent_sound}, specifically Woof!"

3. Полиморфизм (Polymorphism)

Одна и та же функция работает по-разному в зависимости от типа объекта.

class Shape:
    def area(self) -> float:
        raise NotImplementedError

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius
    
    def area(self) -> float:
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height
    
    def area(self) -> float:
        return self.width * self.height

# Полиморфизм — одна функция работает с разными типами
def print_area(shape: Shape) -> None:
    print(f"Area: {shape.area()}")

circle = Circle(5)
rectangle = Rectangle(4, 6)

print_area(circle)       # Area: 78.5
print_area(rectangle)    # Area: 24

# Каждый класс реализует area() по-своему

4. Абстракция (Abstraction)

Скрытие сложности, предоставление простого интерфейса.

from abc import ABC, abstractmethod

# Абстрактный класс — не может быть создан напрямую
class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount: float) -> bool:
        pass
    
    @abstractmethod
    def refund(self, transaction_id: str) -> bool:
        pass

# Конкретные реализации
class StripeProcessor(PaymentProcessor):
    def process_payment(self, amount: float) -> bool:
        # Логика для Stripe
        print(f"Processing ${amount} via Stripe")
        return True
    
    def refund(self, transaction_id: str) -> bool:
        print(f"Refunding {transaction_id} via Stripe")
        return True

class PayPalProcessor(PaymentProcessor):
    def process_payment(self, amount: float) -> bool:
        # Логика для PayPal
        print(f"Processing ${amount} via PayPal")
        return True
    
    def refund(self, transaction_id: str) -> bool:
        print(f"Refunding {transaction_id} via PayPal")
        return True

# Использование
def checkout(processor: PaymentProcessor, amount: float):
    # Не важно какой процессор, интерфейс одинаковый
    processor.process_payment(amount)

stripe = StripeProcessor()
checkout(stripe, 99.99)  # Processing $99.99 via Stripe

Полезные паттерны

Dataclass (для простых данных)

from dataclasses import dataclass
from datetime import datetime

@dataclass
class User:
    id: int
    name: str
    email: str
    created_at: datetime = datetime.now()
    
    # Автоматически генерирует __init__, __repr__, __eq__

# Использование
user = User(1, "Alice", "alice@example.com")
print(user)  # User(id=1, name='Alice', email='alice@example.com', ...)

Singleton паттерн (один экземпляр)

class Database:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

db1 = Database()
db2 = Database()
print(db1 is db2)  # True — одинаковые объекты!

Composition вместо наследования

# Плохо — много наследования
class Dog(Animal, Domesticated, Furry):
    pass

# Хорошо — композиция
class Dog:
    def __init__(self, name: str):
        self.name = name
        self.fur = FurBehavior()
        self.domestication = DomestificationBehavior()
    
    def play(self):
        self.fur.shed()
        self.domestication.obey()

Методы специального назначения (dunder methods)

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
    
    def __str__(self) -> str:
        """Читаемое представление"""
        return f"{self.name} ({self.age} years old)"
    
    def __repr__(self) -> str:
        """Для разработчиков"""
        return f"Person(name='{self.name}', age={self.age})"
    
    def __eq__(self, other) -> bool:
        """Сравнение"""
        return self.name == other.name and self.age == other.age
    
    def __lt__(self, other) -> bool:
        """Сортировка"""
        return self.age < other.age
    
    def __len__(self) -> int:
        """Длина строки имени"""
        return len(self.name)
    
    def __call__(self) -> str:
        """Вызовимый объект"""
        return f"Calling {self.name}"

person = Person("Alice", 30)
print(str(person))      # Alice (30 years old)
print(repr(person))     # Person(name='Alice', age=30)
print(len(person))      # 5
print(person())         # Calling Alice

Важные моменты

  • Класс vs объект: Класс — это шаблон, объект — экземпляр класса
  • self: Первый параметр метода — ссылка на сам объект
  • init: Конструктор, вызывается при создании объекта
  • Приватность: _ (одиночный underscore) — соглашение о приватности, не закон
  • Type hints: Используй для ясности
  • Наследование: Используй для код-переиспользования, но предпочитай композицию

Классы — мощный инструмент для организации кода, но помни о SOLID принципах и избегай over-engineering.

Что такое классы ООП? | PrepBro