Почему
super() не всегда означает «родительский класс» и что оно делает на самом деле.Вы пишете наследование в Python, кажется всё просто… но вывод программы может вас удивить:
class A:
def process(self):
print("A.process()")
class B(A):
def process(self):
print("B.process()")
super().process()
class C(A):
def process(self):
print("C.process()")
super().process()
class D(B, C):
def process(self):
print("D.process()")
super().process()
d = D()
d.process()
Ожидание:
D.process()
B.process()
A.process()
Реальность:
D.process()
B.process()
C.process()
A.process()
Почему
C.process() вызывается, если B наследуется от A? Всё дело в Method Resolution Order (MRO).➡️ MRO — порядок поиска методов
Каждый класс в Python имеет атрибут
__mro__ — последовательность классов, через которую Python ищет методы:class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
# <class '__main__.A'>, <class 'object'>)
Вывод:
super() вызывает следующий класс в MRO, а не «родительский класс».➡️ Алгоритм C3 Linearization
Python использует C3 линейнизацию, чтобы определить MRO. Основные правила:
1️⃣ Дети перед родителями
2️⃣ Порядок слева направо из списка наследников
3️⃣ Сохранять порядок родительских MRO (монотонность)
Пример для
class D(B, C):L(D) = D + merge(L(B), L(C), [B, C])
L(B) = B, A, object
L(C) = C, A, object
merge([B,A,object],[C,A,object],[B,C]) -> D,B,C,A,object
Это гарантирует, что каждый класс вызывается ровно один раз, решая «алмазную проблему».
➡️ Практическая сила super() + MRO
Использование с cooperative mixins:
class LoggingMixin:
def __init__(self, **kwargs):
print("LoggingMixin.__init__")
super().__init__(**kwargs)
class ValidationMixin:
def __init__(self, **kwargs):
print("ValidationMixin.__init__")
super().__init__(**kwargs)
class Base:
def __init__(self, name):
self.name = name
print(f"Base.__init__: {name}")
class Document(LoggingMixin, ValidationMixin, Base):
def __init__(self, name, **kwargs):
print("Document.__init__")
super().__init__(name=name, **kwargs)
doc = Document("report.pdf")
# Output:
# Document.__init__
# LoggingMixin.__init__
# ValidationMixin.__init__
# Base.__init__: report.pdf
Правила для mixins:
1️⃣ Принимать
**kwargs и передавать их дальше2️⃣ Вызывать
super().__init__() всегда3️⃣ Извлекать только свои параметры
Так работает cooperative multiple inheritance: каждый класс выполняется один раз в правильном порядке.
Ошибки, которые часто встречаются:
1️⃣ Несогласованные сигнатуры методов → используйте
**kwargs2️⃣ Неполный вызов
super()→ классы в цепочке могут быть пропущены3️⃣ Ошибочные предположения про родителя → super() зависит от MRO, а не от «родительского класса»
Проверка MRO и super():
print(Document.__mro__)
# (<class 'Document'>, <class 'LoggingMixin'>,
# <class 'ValidationMixin'>, <class 'Base'>, <class 'object'>)
isinstance() и issubclass() также следуют MRO.🔹 Курс «Программирование на языке Python»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
🐸 Библиотека питониста
#буст
