Что такое порядок разрешения методов (MRO) и почему не все наследования имеют смысл?
MRO, в общем, - это способ (Вы можете назвать это стратегией), при котором конкретный язык программирования просматривает верхнюю часть иерархии класса, чтобы найти метод, который ему в настоящее время нужен. Стоит подчеркнуть, что в разных языках используются несколько (или даже совершенно) разные MRO. Однако Python - уникальное существо в этом отношении, и его обычаи немного специфичны.
Мы собираемся показать Вам, как работает MRO Python в двух специфических случаях, которые являются четкими примерами проблем, которые могут возникнуть, когда вы слишком опрометчиво пытаетесь использовать множественное наследование. Начнем с фрагмента, который поначалу может показаться простым. Посмотрите, что мы приготовили для Вас в редакторе.
Мы уверены, что если Вы проанализируете фрагмент самостоятельно, Вы не увидите в нем никаких аномалий. Да, Вы совершенно правы - все выглядит ясно и просто и не вызывает беспокойства. Если Вы запустите код, он выдаст следующий предсказуемый результат:
bottom
middle
top
output
Пока никаких сюрпризов. Внесем небольшие изменения в этот код. Посмотрите:
class Top:
def m_top(self):
print("top")
class Middle(Top):
def m_middle(self):
print("middle")
class Bottom(Middle, Top):
def m_bottom(self):
print("bottom")
object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()
Видите разницу? Она скрывается в этой строке:
class Bottom(Middle, Top):
Таким экзотическим способом мы превратили очень простой код с четким путем одиночного наследования в таинственную загадку множественного наследования. "Действительно?" - Вы можете спросить. Да, это так. "Как такое возможно?" - спросите сейчас, и мы надеемся, что Вы действительно чувствуете необходимость задать этот вопрос.
Как видите, порядок, в котором два суперкласса были перечислены в круглых скобках, соответствует структуре кода: класс Middle
предшествует классу Top
, просто как в реальном пути наследования.
Несмотря на свою странность, код верен и работает так, как ожидалось, но следует отметить, что это обозначение не приносит никаких новых функций или дополнительного смысла.
Давайте еще раз изменим код - теперь мы поменяем местами имена обоих суперклассов в определении класса Bottom
. Вот как сейчас выглядит фрагмент:
class Top:
def m_top(self):
print("top")
class Middle(Top):
def m_middle(self):
print("middle")
class Bottom(Top, Middle):
def m_bottom(self):
print("bottom")
object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()
Чтобы предвидеть ваш вопрос, мы скажем, что эта поправка испортила код, и он больше не будет работать. Какая жалость. Порядок, который мы пытались установить (верхний, средний), несовместим с путем наследования, полученным из структуры кода. Python это не понравится. Вот что мы увидим:
TypeError: Cannot create a consistent method resolution order (MRO) for bases Top, Middle
output
Мы думаем, что сообщение говорит само за себя. MRO Python не может быть искажен или нарушен не только потому, что Python так работает, но и потому, что это правило, которому Вы должны подчиняться.