Как в Python вызвать конкретную реализацию метода, игнорируя порядок MRO?

Ответ

Да, в Python можно явно вызвать реализацию метода из конкретного родительского класса, даже если он переопределен и MRO (Method Resolution Order) по умолчанию выбрал бы другую реализацию. Это делается путем прямого обращения к классу.

Способ: Прямой вызов через класс

Самый простой способ — вызвать метод напрямую у класса-предка, передав экземпляр self в качестве первого аргумента. Это позволяет точно указать, какую версию метода вы хотите использовать.

Пример

class A:
    def process(self):
        print("Processing in A")

class B:
    def process(self):
        print("Processing in B")

# MRO для C: C -> A -> B -> object
class C(A, B):
    def run(self):
        print("--- Running default MRO ---")
        self.process()  # Вызовет A.process() согласно MRO

        print("--- Running specific implementation ---")
        # Явно вызываем реализацию из класса B
        B.process(self)

instance = C()
instance.run()

Вывод:

--- Running default MRO ---
Processing in A
--- Running specific implementation ---
Processing in B

Такой подход полезен в сложных сценариях с множественным наследованием (например, при работе с mixins), когда необходимо вызвать логику из конкретного родителя, а не следовать стандартной цепочке super().

Ответ 18+ 🔞

А, ну ты смотри, какой хитрый вопрос подкинул! Python, блядь, он же как хитрая жопа — вроде всё по правилам, а потом раз, и MRO тебе в мозг вьебался. Так вот, слушай сюда, разбираемся.

Представь, что у тебя есть классы, как эти, блядь, слоёные пироги наследования. И метод один и тот же у нескольких родителей переопределён. MRO по умолчанию, этот умник, идёт по своей цепочке и выбирает первую попавшуюся реализацию. Но что, если тебе, сука, нужна не та, которую он нашёл, а конкретно из другого родителя? Например, из того, кто в цепочке вторым или третьим идёт?

Вот тут-то и пригождается старый добрый прямой вызов через класс. Это как взять и крикнуть: «Эй, ты, класс B, я с тобой разговариваю, а не с твоим старшим братом A!»

Смотри, как это выглядит в коде, тут всё просто, как три копейки:

class A:
    def process(self):
        print("Processing in A")

class B:
    def process(self):
        print("Processing in B")

# MRO для C: C -> A -> B -> object
class C(A, B):
    def run(self):
        print("--- Running default MRO ---")
        self.process()  # Вызовет A.process() согласно MRO

        print("--- Running specific implementation ---")
        # А вот тут мы говорим: "Блядь, хватит, давай по-честному!"
        # Явно вызываем реализацию из класса B, тыкая в него пальцем.
        B.process(self)

instance = C()
instance.run()

И что мы получаем на выходе? А вот эту красоту:

--- Running default MRO ---
Processing in A
--- Running specific implementation ---
Processing in B

Видишь? По умолчанию MRO, этот пиздопроебибна, ломанулся к первому родителю, к A. Но мы-то умнее! Мы сказали: «Нет, дружок, сегодня работаем с B». И просто вызвали B.process(self), передав туда наш экземпляр self. Всё, приехали. Задача решена.

Этот приём — просто палочка-выручалочка в этих ваших миксинах и прочей ебучке с множественным наследованием, когда super() начинает вести себя как кот, сука, собака, и прыгать не туда, куда надо. А тут — раз, и порядок. Чисто, ясно, без лишних танцев с бубном.