Классы
Классы, ООП, Паттерны, SOLID, DDD
Классы в Python
Магические методы
__new__
- инициализирует класс- Юзкейс: Синглтоны
- Юзкейс: Всякие орм/датаклассы
__init__
- проставляет переменные (поля) инстанцу__dict__
- словарь с полями инстанца__slots__
- Юзкейс: Оптимизло памяти
- Удаляет
__dict__
из объекта + нельзя кастомные поля высталять - Если класс Б наследует класс А со слотами, то в Б слотов не будет
Порядок поиска атрибутов в классе
__getattribute__
- всегда- data-descriptors +
@property
self.__dict__
- non-data-descriptors +
__class__.__dict__
__slots__
__getattr__
Атрибуты класса / инстанца
class MyClass:
class_attr = 0
instance_a = MyClass()
instance_b = MyClass()
instance_a.class_attr += 1 # Modifies instance_a's class_attr
print(instance_a.class_attr) # 1
print(instance_b.class_attr) # 0
print(MyClass.class_attr) # 0
Дескрипторы
- Юзкейсы: орм, cached_property
__get__
- non-data-descriptors
__set__
/__delete__
- data-descriptors
class D:
def __set__(self, inst, val):
inst.val_ = val
def __get__(self, inst, owner=None):
return getattr(inst, 'val_', 5)
class C:
d = D()
assert C().d == 5
__set_name__
- имя поля, где выставили дескрипторd = D() => __set_name__('d')
Мета-классы
- Динамические классы
type(cls, bases, params)
Как функция:
def create_class_point(name, base, attrs):
attrs.update({'MAX_COORD': 100})
return type(name, bases, params)
class Point(metaclass=create_class_point):
...
pt = Point()
assert pt.MAX_COORD == 100
Как класс:
class Meta(type):
def __new__(cls, name, base, attrs):
attrs.update({'MAX_COORD': 100})
return type.__new__(cls, name, base, attrs)
class Point(metaclass=Meta):
...
ООП
- Зачем нужны классы / ООП - чтоб было состояние и все в одном месте генерализовано
Принципы
- Абстракция - используем только нужные данные, ненужное не описываем
- Инкапсуляция - сокрытие инфы, публичные методы
- Наследование - общие свойства уносим в родителя
- (Параметрический) Полиморф - чтоб параметры реализовывали интерфейс который нам нужен / наследование
Наследование
- Миксин - расширение классов
- Diamond Problem:
ACls, BCls(ACls), CCls(ACls), D(B, C)
- MRO:
D > B, C > A
- Доступ к приватным
a._A__private
-
Связанность vs Связность
- Связанность - использование классов в классах
- Связность - сколько ответственностей у класса
- Абстр. классы vs Интерфейсы
- Абстр. классы для исключений
- Целостность данных: Как получить юзера с заполненными полями
- Сделаем дто с обяз. полями
Паттерны
Порождающие / Creational
Фабричный метод
- Создание объекта через метод (обычно
@classmethod
)
Абстрактная фабрика
- Как фабрика, только для нескольких объектов
Строитель (Builder)
- Последовательное создание объекта вместо конструктора с 10 арг:
return self
Прототип
- Копирование объекта:
copy.deepcopy
Singleton
- Класс который можно создать только 1 раз (1 инстанц):
__new__
Структурные
Адаптер
- Класс адаптирующий другой класс под интерфейс
- Как адаптер на розетку
Мост (Brigde)
- Абстракция, использующая интерфейсы
- Типа есть репорт-генератор, принимающий класс-лоадер данных, и репорты могут быть разными и лоадеры могут быть разными
Компоновщик (Composite)
- Тупа дерево
Декоратор
- Враппинг - добавления поведения с сохранением совместимости
Фасад
- Скрытие сложного апи
Flyweight
- Кеширование
Proxy
- Тот же враппер
__getattr__
Chain of Responsobility
- Передача ответственности другому классу, если текущий класс не в кассу, по цепочке
Поведенческие
Команда
- oop callback:
btn.on_click(cb) -> btn = Btn(Command); btn.on_click()
- do / undo
Итератор
__iter__
Медиатор
- Общение между объектами через спец-класс, а не напрямую
Memento
- Возможность отката
Observer
- Уведомление подписчиков о событии
Strategy
- Выбор поведения по условию
Visitor
- Единый интерфейс для разных объектов с одинаковым интерфейсом
SOLID
S - Single Responsibility
- Не перегружаем классы, должна быть 1 ответственность
O - Open Close
- Открыт для расширения, закрыт для модификации
- Написали класс - больше не меняем его (не добавляем новые функции), юзаем наследование/композицию
- Вместо класса с кучей методов, делаем кучу маленьких классов
L - Liskov Substitute
- Ожидания от наследников = ожидания от родителей
I - Interface Segregation
- Маленькие интерфейсы круче, потому что большие не всегда могут быть реализованы
D - Dependency Inversion
- Интерфейсы над реализацией
DDD
- Страна это Value Object или Entity
- It depends
Материалы
- https://www.youtube.com/playlist?list=PLBheEHDcG7-k0eMcAdrzNFgYBf9jua3va
- https://www.youtube.com/watch?v=ruOzNI9Q-Ho
- https://www.youtube.com/watch?v=2d3vZhF-2aA
- https://www.youtube.com/watch?v=vBys0SwYvCQ
- https://www.youtube.com/playlist?list=PLA0M1Bcd0w8zPwP7t-FgwONhZOHt9rz9E
- https://www.youtube.com/watch?v=_A90qAV_P5U
- EngineerSpock - IT & программирование — Шаблоны проектирования на Python
- Github: faif - python-patterns