08. Расширенные возможности ООП

Расширенные возможности ООП

Описание: Этот урок поможет вам понять, как устроено объектно-ориентированное программирование (ООП) в Python на более глубоком уровне. Вы научитесь правильно применять наследование, скрывать внутренние данные, использовать абстрактные классы и реализовывать гибкое поведение через полиморфизм.

1. Напоминание: что такое ООП

ООП — это способ организации кода, при котором программа строится из объектов, у которых есть:
  • состояние (атрибуты, переменные внутри объекта);
  • поведение (методы, которые выполняют действия).

Ключевые принципы ООП:

  • Инкапсуляция — скрытие деталей реализации.
  • Наследование — использование кода родительских классов.
  • Полиморфизм — единый интерфейс для разных типов объектов.
  • Абстракция — выделение только сути без деталей.

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

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

class User:
    def __init__(self, name):
        self.name = name

def info(self): print(f'Пользователь: {self.name}')

class Admin(User): # Наследуем от User def delete_user(self, user): print(f'{self.name} удалил пользователя {user.name}')

user1 = User('Анна') admin = Admin('Сергей') admin.info() # Использует метод родителя admin.delete_user(user1)

Вывод:


Пользователь: Сергей
Сергей удалил пользователя Анна

Один класс может наследовать методы и атрибуты другого, а также добавлять свои собственные.

3. Переопределение методов

Если нужно изменить поведение родительского метода — просто переопредели его в дочернем классе:

class User:
    def info(self):
        print('Обычный пользователь')

class Admin(User): def info(self): print('Администратор с полными правами')

users = [User(), Admin()] for u in users: u.info() # Полиморфизм в действии

Вывод:


Обычный пользователь
Администратор с полными правами

4. Инкапсуляция

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

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance  # приватное поле

def deposit(self, amount): if amount > 0: self.__balance += amount

def withdraw(self, amount): if 0 < amount <= self.__balance: self.__balance -= amount else: print('Недостаточно средств!')

def get_balance(self): return self.__balance

acc = BankAccount('Иван', 100) acc.deposit(50) print(acc.get_balance()) # 150 acc.__balance = 999999 # Не изменит реальный баланс! print(acc.get_balance()) # 150

Двойное подчёркивание делает переменную «приватной», и она становится недоступной напрямую из вне.

5. Свойства (property)

Иногда нужно контролировать доступ к данным, но при этом обращаться к ним как к обычным атрибутам.

class Product:
    def __init__(self, price):
        self._price = price

@property def price(self): return self._price

@price.setter def price(self, value): if value > 0: self._price = value else: raise ValueError('Цена должна быть положительной')

p = Product(100) print(p.price) p.price = 250 print(p.price)

Используйте @property, чтобы сделать код безопаснее и элегантнее.

6. Полиморфизм

Полиморфизм позволяет использовать один и тот же метод для разных типов объектов.

class Dog:
    def speak(self):
        return 'Гав!'

class Cat: def speak(self): return 'Мяу!'

animals = [Dog(), Cat()] for a in animals: print(a.speak())

Вывод:


Гав!
Мяу!

Один интерфейс — разное поведение. Это и есть полиморфизм.

7. Абстрактные классы

Абстрактные классы задают общий интерфейс, который обязаны реализовать дочерние классы.

from abc import ABC, abstractmethod

class Shape(ABC): @abstractmethod def area(self): pass

class Circle(Shape): def __init__(self, r): self.r = r

def area(self): return 3.14 * self.r ** 2

class Rectangle(Shape): def __init__(self, w, h): self.w, self.h = w, h

def area(self): return self.w * self.h

shapes = [Circle(5), Rectangle(4, 6)] for s in shapes: print(s.area())

Вывод:


78.5
24

  • Абстрактный класс нельзя создать напрямую.
  • Он определяет обязательные методы для наследников.

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

Класс может наследоваться сразу от нескольких родителей.

class Logger:
    def log(self, message):
        print(f'[LOG]: {message}')

class Saver: def save(self, filename): print(f'Сохраняем данные в {filename}')

class Report(Logger, Saver): def generate(self): self.log('Создаём отчёт...') self.save('report.txt')

r = Report() r.generate()

Вывод:


[LOG]: Создаём отчёт...
Сохраняем данные в report.txt

9. Пример мини-проекта: система уведомлений

Создадим систему, где разные типы уведомлений (email, SMS, push) реализуют общий интерфейс.

from abc import ABC, abstractmethod

class Notifier(ABC): @abstractmethod def send(self, message): pass

class EmailNotifier(Notifier): def send(self, message): print(f'Отправлено на e-mail: {message}')

class SMSNotifier(Notifier): def send(self, message): print(f' SMS отправлено: {message}')

class PushNotifier(Notifier): def send(self, message): print(f' Push-уведомление: {message}')

# Используем полиморфизм notifiers = [EmailNotifier(), SMSNotifier(), PushNotifier()] for n in notifiers: n.send('Обновление системы успешно установлено!')

Вывод:


Отправлено на e-mail: Обновление системы успешно установлено!
 SMS отправлено: Обновление системы успешно установлено!
Push-уведомление: Обновление системы успешно установлено!

10. Лучшие практики ООП

  • Используйте инкапсуляцию, чтобы защитить внутренние данные.
  • Не злоупотребляйте наследованием — применяйте его, если действительно нужно.
  • Используйте абстрактные классы и интерфейсы для расширяемости кода.
  • Пишите читаемые имена методов и классов — это часть хорошего дизайна.

Итоги урока

Теперь вы умеете:
  • Применять наследование и переопределение методов.
  • Использовать инкапсуляцию и property для защиты данных.
  • Создавать абстрактные классы и интерфейсы.
  • Использовать полиморфизм в реальных задачах.