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

Приведи примеры с ООП в Python

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

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

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

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

Примеры ООП в Python

Объектно-ориентированное программирование (ООП) — это парадигма, которая организует код вокруг объектов, имеющих состояние (атрибуты) и поведение (методы). Рассмотрим примеры от базовых до продвинутых.

1. Основные концепции

Класс и объект

# Класс - это шаблон
class Car:
    # Атрибут класса (общий для всех объектов)
    wheels = 4
    
    # Конструктор - вызывается при создании объекта
    def __init__(self, brand: str, model: str, speed: float):
        # Атрибуты объекта (индивидуальные)
        self.brand = brand
        self.model = model
        self.speed = speed
        self.is_running = False
    
    # Методы
    def start(self):
        """Запустить автомобиль"""
        self.is_running = True
        print(f"{self.brand} {self.model} запущен")
    
    def stop(self):
        """Остановить автомобиль"""
        self.is_running = False
        print(f"{self.brand} {self.model} остановлен")
    
    def drive(self, distance: float):
        """Ехать на определённое расстояние"""
        if not self.is_running:
            print(f"Сначала запустите автомобиль")
            return
        
        time_required = distance / self.speed
        print(f"Проехали {distance}км за {time_required:.1f} часов")

# Создаём объекты (экземпляры класса)
car1 = Car("Toyota", "Camry", 180)
car2 = Car("BMW", "X5", 220)

# Используем объекты
car1.start()
car1.drive(100)
car1.stop()

print(f"Количество колес: {car1.wheels}")
print(f"Количество колес: {car2.wheels}")

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

Наследование позволяет создавать новые классы на основе существующих.

# Базовый класс (родитель)
class Vehicle:
    def __init__(self, brand: str):
        self.brand = brand
        self.is_running = False
    
    def start(self):
        self.is_running = True
        print(f"{self.brand} запущен")
    
    def stop(self):
        self.is_running = False
        print(f"{self.brand} остановлен")

# Класс-наследник (потомок)
class Car(Vehicle):
    def __init__(self, brand: str, model: str, num_doors: int):
        # Вызываем конструктор родителя
        super().__init__(brand)
        self.model = model
        self.num_doors = num_doors
    
    # Переопределяем (override) метод родителя
    def start(self):
        super().start()  # Вызываем метод родителя
        print(f"Двигатель {self.model} прогревается")
    
    # Новый метод, уникальный для Car
    def open_trunk(self):
        print(f"Открыли багажник {self.model}")

# Ещё один наследник
class Motorcycle(Vehicle):
    def __init__(self, brand: str, has_sidecar: bool):
        super().__init__(brand)
        self.has_sidecar = has_sidecar
    
    def wheelie(self):
        if not self.is_running:
            print("Запустите мотоцикл!")
            return
        print(f"{self.brand} встал на заднее колесо!")

# Использование
car = Car("Toyota", "Camry", 4)
car.start()
car.open_trunk()
car.stop()

moto = Motorcycle("Harley", False)
moto.start()
moto.wheelie()
moto.stop()

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

Скрывание деталей реализации и предоставление контролируемого доступа.

class BankAccount:
    def __init__(self, owner: str, balance: float):
        self.owner = owner
        # Private атрибут (по соглашению, начинается с _)
        self._balance = balance
        # Очень private (name mangling)
        self.__pin = "1234"
    
    # Public метод
    def deposit(self, amount: float):
        """Пополнить счёт"""
        if amount > 0:
            self._balance += amount
            print(f"Пополнено на {amount}. Новый баланс: {self._balance}")
        else:
            print("Сумма должна быть больше нуля")
    
    # Public метод с проверкой
    def withdraw(self, amount: float, pin: str):
        """Снять деньги со счёта"""
        if not self._verify_pin(pin):
            print("Неверный PIN")
            return
        
        if amount > self._balance:
            print("Недостаточно средств")
            return
        
        self._balance -= amount
        print(f"Снято {amount}. Остаток: {self._balance}")
    
    # Property - управляемый доступ к атрибутам
    @property
    def balance(self):
        """Получить баланс (читать можно, менять нельзя)"""
        return self._balance
    
    # Private метод
    def _verify_pin(self, pin: str) -> bool:
        """Проверить PIN (не вызывается снаружи)"""
        return pin == self.__pin
    
    def __str__(self):
        return f"Счёт {self.owner}: {self._balance} руб"

# Использование
account = BankAccount("John", 10000)
print(account)  # Счёт John: 10000 руб

account.deposit(5000)
account.withdraw(3000, "1234")

# Это работает
print(account.balance)  # 12000

# Это НЕ работает (по соглашению)
# account._balance = 1000000  # НЕ ДЕЛАЙТЕ ТАК!

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

Один интерфейс, разные реализации.

# Базовый класс
class Animal:
    def speak(self):
        pass
    
    def move(self):
        pass

# Разные реализации
class Dog(Animal):
    def speak(self):
        return "Гав! Гав!"
    
    def move(self):
        return "Бежит на четырёх лапах"

class Cat(Animal):
    def speak(self):
        return "Мяу"
    
    def move(self):
        return "Прыгает и ходит изящно"

class Duck(Animal):
    def speak(self):
        return "Кря-кря"
    
    def move(self):
        return "Плывёт в воде"

# Полиморфизм в действии
def animal_concert(animals: list):
    for animal in animals:
        print(f"{animal.__class__.__name__}: {animal.speak()}")
        print(f"{animal.__class__.__name__}: {animal.move()}")
        print()

# Один код, разные объекты
animals = [Dog(), Cat(), Duck()]
animal_concert(animals)

# Вывод:
# Dog: Гав! Гав!
# Dog: Бежит на четырёх лапах
# 
# Cat: Мяу
# Cat: Прыгает и ходит изящно
# ...

5. Абстрактные классы (Abstract Classes)

Классы, которые нельзя инстанцировать, они только для наследования.

from abc import ABC, abstractmethod

# Абстрактный класс
class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        """Вычислить площадь (должен быть реализован в подклассах)"""
        pass
    
    @abstractmethod
    def perimeter(self) -> float:
        """Вычислить периметр"""
        pass
    
    # Обычный метод в абстрактном классе
    def describe(self):
        print(f"{self.__class__.__name__}: S={self.area():.1f}, P={self.perimeter():.1f}")

# Реализации
class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius
    
    def area(self) -> float:
        return 3.14 * self.radius ** 2
    
    def perimeter(self) -> float:
        return 2 * 3.14 * self.radius

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 perimeter(self) -> float:
        return 2 * (self.width + self.height)

# Использование
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
    shape.describe()

# Это НЕ работает - абстрактный класс нельзя инстанцировать
# shape = Shape()  # TypeError: Can't instantiate abstract class

6. Множественное наследование

class Flyer:
    def fly(self):
        return "Летит в небе"

class Swimmer:
    def swim(self):
        return "Плывёт в воде"

class Walker:
    def walk(self):
        return "Ходит по земле"

# Один класс наследует от нескольких
class Duck(Flyer, Swimmer, Walker):
    def quack(self):
        return "Кря-кря"

# MRO - Method Resolution Order (порядок поиска методов)
print(Duck.__mro__)  # Порядок поиска методов

duck = Duck()
print(duck.fly())      # Летит в небе
print(duck.swim())     # Плывёт в воде
print(duck.walk())     # Ходит по земле
print(duck.quack())    # Кря-кря

7. Композиция (Composition)

Использование объектов других классов внутри класса.

class Engine:
    def __init__(self, power: int):
        self.power = power
    
    def start(self):
        return f"Двигатель {self.power}л запущен"

class Wheel:
    def __init__(self, size: int):
        self.size = size
    
    def rotate(self):
        return f"Колесо {self.size}' вращается"

class Car:
    def __init__(self, brand: str, engine: Engine):
        self.brand = brand
        self.engine = engine
        self.wheels = [Wheel(18) for _ in range(4)]
    
    def start(self):
        print(self.engine.start())
        for i, wheel in enumerate(self.wheels):
            print(f"Колесо {i+1}: {wheel.rotate()}")

# Использование
engine = Engine(3)
car = Car("BMW", engine)
car.start()

# Композиция предпочтительнее наследования в Python:
# "Composition over Inheritance"

8. Magic Methods (Дандер методы)

class Vector:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y
    
    # Строковое представление
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
    
    # Для разработчиков (более полная информация)
    def __repr__(self):
        return f"Vector(x={self.x}, y={self.y})"
    
    # Сложение векторов
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    # Вычитание
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)
    
    # Умножение на число
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    
    # Сравнение
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    
    # Длина вектора
    def __len__(self):
        return int((self.x**2 + self.y**2)**0.5)
    
    # Хешируемость
    def __hash__(self):
        return hash((self.x, self.y))

# Использование
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1)              # Vector(3, 4)
print(v1 + v2)         # Vector(4, 6)
print(v1 - v2)         # Vector(2, 2)
print(v1 * 2)          # Vector(6, 8)
print(v1 == v2)        # False
print(len(v1))         # 5 (гипотенуза)

# Использование как ключ словаря
positions = {v1: "point1", v2: "point2"}

9. Декораторы класса

from dataclasses import dataclass
from typing import List

# Автоматически генерирует __init__, __repr__, __eq__ и т.д.
@dataclass
class User:
    id: int
    name: str
    email: str
    tags: List[str] = None  # Со значением по умолчанию
    
    def __post_init__(self):
        if self.tags is None:
            self.tags = []

# Использование
user1 = User(1, "John", "john@example.com")
user2 = User(1, "John", "john@example.com")

print(user1)            # User(id=1, name='John', email='john@example.com', tags=[])
print(user1 == user2)   # True (автоматическое сравнение)

10. Контекстные менеджеры (Context Managers)

class DatabaseConnection:
    def __init__(self, host: str):
        self.host = host
        self.connected = False
    
    def __enter__(self):  # Вход в контекст (with ...)
        print(f"Подключение к {self.host}...")
        self.connected = True
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):  # Выход из контекста
        print(f"Отключение от {self.host}")
        self.connected = False
        
        # Возвращаем True, чтобы подавить исключение
        if exc_type:
            print(f"Ошибка: {exc_val}")
        return False
    
    def query(self, sql: str):
        if not self.connected:
            raise RuntimeError("Не подключено")
        return f"Результат: {sql}"

# Использование with (автоматический __enter__ и __exit__)
with DatabaseConnection("localhost") as db:
    print(db.query("SELECT * FROM users"))
# Автоматически закрывается!

Best Practices в ООП

# ✅ Хорошее ООП
class GoodExample:
    """Один класс - одна ответственность"""
    
    def __init__(self, name: str):
        self._name = name
    
    @property
    def name(self) -> str:
        return self._name
    
    def do_something(self):
        pass

# ❌ Плохое ООП
class BadExample:
    """Слишком много ответственности"""
    
    def __init__(self):
        self.users = []
        self.products = []
        self.orders = []
    
    def add_user(self): pass
    def remove_user(self): pass
    def add_product(self): pass
    def remove_product(self): pass
    def create_order(self): pass
    def process_payment(self): pass
    def send_email(self): pass
    # и 20 других методов...

Заключение

ООП в Python позволяет:

  • Организовать код вокруг сущностей предметной области
  • Переиспользовать код через наследование и композицию
  • Скрывать деталями реализации через инкапсуляцию
  • Создавать гибкий код через полиморфизм
  • Писать чистый код следуя SOLID принципам

Железное правило: Используй ООП когда это упрощает код, а не когда усложняет. Python поддерживает разные парадигмы - используй то, что подходит к задаче.