Основные принципы ООП
Объектно-ориентированное программирование (ООП) базируется на четырех ключевых принципах:
1. Инкапсуляция — объединение данных и методов в единый объект, ограничение прямого доступа к внутреннему состоянию.
2. Наследование — возможность создания новых классов на основе существующих.
3. Полиморфизм — использование объектов разных классов через единый интерфейс.
4. Абстракция — выделение существенных характеристик объекта, игнорирование несущественных.
Синтаксис класса
Класс — шаблон для создания объектов. Определяется ключевым словом `class`:
class Car:
....# Атрибут класса
....wheels = 4
....def __init__(self, model):
........# Атрибут экземпляра
........self.model = model
# Создание экземпляра
my_car = Car("Tesla")
Наследование
Дочерний класс наследует атрибуты и методы родительского:
class ElectricCar(Car):
....def __init__(self, model, battery):
........super().__init__(model)
........self.battery = battery
tesla = ElectricCar("Model S", 100)
Класс vs Экземпляр
- Класс — шаблон с общими атрибутами и методами.
- Экземпляр — конкретный объект, созданный по шаблону класса.
Перегрузка операторов
Используйте "магические" методы для изменения поведения операторов:
class Vector:
....def __init__(self, x, y):
........self.x = x
........self.y = y
....def __add__(self, other):
........return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(2, 3)
v2 = Vector(1, 4)
result = v1 + v2 # Vector(3, 7)
Вызываемые объекты (__call__)
Позволяет вызывать экземпляры как функции:
class Adder:
....def __call__(self, a, b):
........return a + b
add = Adder()
print(add(2, 3)) # 5
Dunder-методы (Double Underscore)
Методы с двойным подчеркиванием управляют поведением объектов:
- __init__: Инициализация экземпляра.
- __new__: Создание экземпляра (вызывается перед `__init__`).
- __del__: Деструктор.
- __repr__, __str__: Строковое представление.
- __eq__, __lt__: Операторы сравнения.
- __hash__: Хэш для использования в словарях.
Атрибуты класса и экземпляра
class Dog:
....species = "Canis lupus" # Атрибут класса
....def __init__(self, name):
........self.name = name # Атрибут экземпляра
print(Dog.species) # Доступ через класс
buddy = Dog("Buddy")
print(buddy.name) # Доступ через экземпляр
Декоратор @property
Превращает метод в атрибут с контролем доступа:
class Circle:
....def __init__(self, radius):
........self._radius = radius
....@property
....def radius(self):
........return self._radius
....@radius.setter
....def radius(self, value):
........if value > 0:
............self._radius = value
Принцип DRY (Don't Repeat Yourself)
Избегайте дублирования кода через наследование, композицию или миксины.
Абстрактные базовые классы (ABC)
from abc import ABC, abstractmethod
class Shape(ABC):
....@abstractmethod
....def area(self):
........pass
class Square(Shape):
....def area(self):
........return side ** 2
Динамический доступ к атрибутам (getattr, setattr)
obj = MyClass()
attr_name = "value"
setattr(obj, attr_name, 10)
print(getattr(obj, attr_name)) # 10
Слоты (__slots__)
Оптимизация памяти за счет фиксированного набора атрибутов:
class Point:
....__slots__ = ('x', 'y') # Запрещает добавление новых атрибутов
....def __init__(self, x, y):
........self.x = x
........self.y = y
MRO и "Алмазная проблема"
Порядок разрешения методов (MRO) определяет порядок поиска методов при наследовании. Алгоритм C3 решает конфликты:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.mro()) # [D, B, C, A, object]
Миксины и Композиция
Миксины — классы, добавляющие функциональность через множественное наследование:
class JsonMixin:
....def to_json(self):
........import json
........return json.dumps(self.__dict__)
class User(JsonMixin):
....def __init__(self, name):
........self.name = name
Композиция предпочтительнее наследования, когда нет явной иерархии.
SOLID принципы
1. Single Responsibility: Класс должен иметь одну причину для изменения.
2. Open-Closed: Классы открыты для расширения, но закрыты для модификации.
3. Liskov Substitution: Подтипы должны быть заменяемы базовыми типами.
4. Interface Segregation: Много специализированных интерфейсов лучше одного общего.
5. Dependency Inversion: Зависимости на абстракциях, а не деталях.
ABC vs Исключения
- ABC гарантируют реализацию методов на этапе создания класса.
- Исключения подходят для обработки ошибок времени выполнения.
Протокол дескрипторов
Дескрипторы управляют доступом к атрибутам:
class PositiveNumber:
....def __set__(self, instance, value):
........if value < 0:
............raise ValueError("Must be positive")
........instance.__dict__[self.name] = value
class MyClass:
number = PositiveNumber()
Метаклассы
Классы, создающие другие классы:
class Meta(type):
....def __new__(cls, name, bases, dct):
........dct['version'] = 1.0
........return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=Meta):
....pass
print(MyClass.version) # 1.0
Заключение
Понимание принципов ООП в Python позволяет создавать гибкие и поддерживаемые приложения. От базового синтаксиса до продвинутых концепций вроде метаклассов — каждый инструмент служит для решения конкретных задач. Практикуйтесь, экспериментируйте, и ваши программы станут элегантнее!