Вопрос с собеседования · Python

Как работают наследование, super() и MRO?

JuniorООП
Ещё спрашивают так:
  • Что такое 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 запрещено

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования