Skip to content

Классы

Классы, ООП, Паттерны, SOLID, DDD

Классы в Python

Магические методы

  • __new__ - инициализирует класс
    • Юзкейс: Синглтоны
    • Юзкейс: Всякие орм/датаклассы
  • __init__ - проставляет переменные (поля) инстанцу
  • __dict__ - словарь с полями инстанца
  • __slots__
    • Юзкейс: Оптимизло памяти
    • Удаляет __dict__ из объекта + нельзя кастомные поля высталять
    • Если класс Б наследует класс А со слотами, то в Б слотов не будет

Порядок поиска атрибутов в классе

  1. __getattribute__ - всегда
  2. data-descriptors + @property
  3. self.__dict__
  4. non-data-descriptors + __class__.__dict__
  5. __slots__
  6. __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
class D:
  def __get__(self, inst, owner=None):
    return 5

class C:
  d = D()

assert C().d == 5
  • __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

Материалы