🔍 Что такое MRO на самом деле?
MRO (Method Resolution Order) — это алгоритм, который определяет порядок поиска методов и атрибутов при множественном наследовании.
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>,
# <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d = D()
d.method() # Вывод: "B" (первый в MRO)
🎯 Как Python вычисляет MRO?
Используется алгоритм C3 Linearization. Главные правила:
• Дочерний класс всегда перед родительским
• Порядок наследования сохраняется
• Более левые классы в наследовании имеют приоритет
⚔️ Разрешение конфликтов методов
Рассмотрим ситуацию, когда несколько родительских классов имеют методы с одинаковыми именами, и как можно управлять их вызовом:
class Printer:
def action(self):
return "Печатаю"
class Scanner:
def action(self):
return "Сканирую"
class Copywriter:
def action(self):
return "Копирую"
class MFP(Printer, Scanner, Copywriter):
pass
🔍 1-й вариант: Оставить как есть (используется первый по MRO)
mfp = MFP()
print(mfp.action()) # 👉 "Печатаю"
Почему? MRO для класса MFP: MFP → Printer → Scanner → Copywriter → object. Python находит метод action() в классе Printer первым и останавливается.
🎯 2-й вариант: явный вызов метода из конкретного класса
print(Scanner.action(mfp)) # 👉 "Сканирую"
Важно: При таком вызове нужно явно передавать объект (mfp) первым аргументом, так как мы вызываем метод класса, а не объекта.
🔧 3-й вариант: Переопределить метод в дочернем классе
class MFP(Printer, Scanner, Copywriter):
def action(self):
return Scanner.action(self)
# Своя уникальная логика
# return "Я МФУ, делаю всё!"
mfp2 = MFP()
print(mfp2.action()) # "Копирую"
📊 Когда что использовать:
1-й способ — когда порядок MRO вас устраивает и конфликтов нет
2-й способ — для разового вызова метода из конкретного родителя
3-й способ — для постоянного изменения поведения или комбинирования логики
⚠️ Замечание:
Хотя явный вызов методов через Класс.метод(объект) работает, это жёсткая привязка к конкретному классу. Если изменится иерархия наследования, такой код может сломаться. Переопределение метода в дочернем классе — более гибкий и поддерживаемый подход.
💡Напоминаю - проверить MRO можно всегда:
print(MFP.__mro__)
# (<class '__main__.MFP'>, <class '__main__.Printer'>,
# <class '__main__.Scanner'>, <class '__main__.Copywriter'>, <class 'object'>)
Вывод: В Python есть гибкие механизмы для работы с наследованием. Выбирайте подход в зависимости от задачи — от простого использования MRO до полного контроля через переопределение методов.