Как работают наследование, super() и MRO?
- Что такое MRO в Python?
- Как работает множественное наследование?
- Что делает super()?
- Что такое ромбовидное наследование?
MRO — порядок, в котором Python ищет метод по иерархии классов; super() вызывает следующий класс в MRO.
- MRO строится алгоритмом C3, смотреть:
Class.__mro__илиClass.mro() super()— не «родитель», а следующий по MRO; это важно при множественном наследовании
А ещё Ромб решается тем, что общий предок в MRO встречается один раз
Подробно
При наследовании класс получает атрибуты и методы родителей и может их переопределить. Когда вызывается метод, Python ищет его по MRO (method resolution order) — линейному списку классов: сам класс, затем родители, в конце object.
При множественном наследовании MRO строится алгоритмом C3. Он гарантирует, что класс идёт раньше своих родителей, порядок родителей из объявления сохраняется, и каждый класс встречается один раз. Поэтому «ромб» (B и C наследуют A, D наследует B и C) не вызывает метод A дважды: MRO будет D, B, C, A, object.
super() возвращает объект, который ищет методы, начиная со следующего класса после текущего в MRO того объекта, для которого вызван метод. Поэтому в B.__init__ вызов super().__init__() при создании D пойдёт в C, а не сразу в A. Это позволяет строить «кооперативное» наследование: каждый класс вызывает super(), и вся цепочка отрабатывает по одному разу.
На практике множественное наследование используют для миксинов — небольших классов с одной добавкой поведения.
Пример
class A:
def hello(self):
return "A"
class B(A):
def hello(self):
return "B" + super().hello()
class C(A):
def hello(self):
return "C" + super().hello()
class D(B, C):
pass
D.__mro__ # (D, B, C, A, object)
D().hello() # 'BCA' — super() в B ведёт в C, а не в AЧто хотят услышать
- Определение MRO и как его посмотреть.
super()— следующий в MRO, а не просто родитель.- Как решается ромбовидное наследование.
- Применение: миксины, кооперативные
__init__.
Частые ошибки
- Утверждать, что
super()всегда вызывает непосредственного родителя. - Вызывать родителя напрямую (
A.__init__(self)) в множественном наследовании — метод может выполниться дважды.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗super() всегда вызывает непосредственного родителя, указанного первым в объявлении класса
- ✓MRO — порядок поиска метода по иерархии (C3); super() вызывает следующий класс в MRO
- ✗При ромбовидном наследовании общий предок вызывается дважды
- ✗Множественное наследование в Python запрещено
Что спросят дальше
Разбор темы
- ООП в Python — классы, свойства, наследование и dataclass
self и __init__, магические методы, property, staticmethod и classmethod, MRO и super, dataclass против namedtuple и Pydantic, ABC и Protocol, метаклассы.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.