Расширенные темы ООП

MRO (Method Resolution Order) и проблема ромбовидного наследования.

подробнее

MRO (Method Resolution Order) — порядок, в котором Python ищет методы и атрибуты в иерархии классов.
Использует алгоритм 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]

Проблема ромбовидного наследования:
Когда класс наследуется от двух классов, имеющих общего предка, возможны дубликаты вызова методов предка.
Решение в Python: MRO гарантирует, что общий предок вызывается один раз (через super()).

class A:
    def show(self): 
        print("A")

class B(A):
    def show(self):
        super().show()
        print("B")

class C(A):
    def show(self):
        super().show()
        print("C")

class D(B, C):
    def show(self):
        super().show()
        print("D")

D().show()
# A → C → B → D  (по MRO, A вызывается 1 раз)

Что такое метаклассы?

подробнее

В Python всё является объектами, включая классы. У каждого класса есть свой метакласс - класс, который создаёт этот класс.
Метакласс - это класс, который создает другие классы. Базовым метаклассом является type, от которого наследуются все другие метаклассы.

При создании класса передаются три аргумента: имя класса (name->Class.__name__), базовые классы (bases->Class.__bases__) и словарь атрибутов (namespace->Class.__dict__). Метакласс отвечает за обработку этих данных и создание самого класса.

Для подавляющего большинства задач использование метаклассов избыточно. Часто можно обойтись декораторами классов, магическим методом __init_subclass__ или дескрипторами.

Пример:
Когда мы указываем metaclass при объявлении класса, вызывается метод __new__ указанного метакласса,
в который передаётся имя класса, кортеж его базовых классов, список его атрибутов и дополнительные именованные аргументы, указанные при объявлении класса.

# Объявляем метакласс
class MyMetaClass(type):
    def __new__(mcs, name, bases, namespace):
        print("Создание объекта класса")
        return super().__new__(mcs, name, bases, namespace)


# Применение метакласса выглядит следующим образом
class Foo(metaclass=MyMetaClass):
    pass

# Сразу после объявления класса Foo будет напечатан следующий текст
# Создание объекта класса

Метакласс может управлять процессом создания класса и:

  • Изменять имя класса (__name__)
  • Добавлять/удалять/модифицировать атрибуты и методы
  • Изменять список базовых классов (__bases__)
  • Контролировать наследование
  • Валидировать определение класса
  • Автоматически регистрировать классы (например, в реестре)
  • Добавлять служебные атрибуты (например, __slots__)
  • Изменять поведение класса до его создания

Что такое классы-миксины?

подробнее

Миксины (Mixin) - это небольшие классы, предназначенные исключительно для “подмешивания” функционала другим классам с помощью множественного наследования.
У миксинов узкая специализация. Они реализуют только одну конкретную задачу (например, логирование, сериализацию в JSON или проверку прав доступа).

Пример

class JsonMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__)

class User(JsonMixin): # "Подмешиваем" функционал JSON
    def __init__(self, name):
        self.name = name

user = User("Ivan")
print(user.to_json()) # Метод пришел из миксина

Что такое сериализация и десериализация?

подробнее

Это процессы превращения данных из живых объектов программы в формат для хранения/передачи и обратно.

  • Сериализация - упаковка объекта (списка, словаря, экземпляра класса) в поток байтов или строку (например, JSON), чтобы сохранить его в файл или отправить по сети.
  • Десериализация - восстановление объекта из этого потока обратно в оперативную память в исходном виде.

Другими словами:

  • Сериализация - python obj -> поток байтов или строка
  • Десериализация - поток байтов или строка -> python obj

Методы (модули json и pickle)

  • Сериализация
    • dump(obj, file) - в файл
    • dumps(obj) - в строку/байты (s = string)
  • Десериализация
    • load(file) - читает из файла
    • loads(data) - Из строки/байтов (s = string)