TypeError: невозможно создать согласованный порядок разрешения метода (MRO)
Это код, который я планирую использовать для своей игры. Но он жалуется на ошибку MRO. Я не знаю почему. Может ли кто-то объяснить мне? Большое спасибо.
class Player:
pass
class Enemy(Player):
pass
class GameObject(Player, Enemy):
pass
g = GameObject()
Ответы
Ответ 1
Ваш GameObject
наследует от Player
и Enemy
. Поскольку Enemy
уже наследуется от Player
, Python теперь не может определить, какой класс сначала будет искать методы; либо Player
, либо на Enemy
, что переопределит все, что определено в Player
.
Здесь вам не нужно указывать все базовые классы Enemy
; просто наследуем от этого одного класса:
class GameObject(Enemy):
pass
Enemy
уже включает Player
, вам не нужно включать его снова.
Ответ 2
Я объясню причину, по которой исходный код не работает.
Python должен решить, в каком порядке искать (прямые и косвенные) базовые классы при поиске атрибута/метода экземпляра. Он делает это путем линеаризации графика наследования, то есть путем преобразования графика базовых классов в последовательность, используя алгоритм, называемый C3 или MRO. Алгоритм MRO - это уникальный алгоритм, который обеспечивает несколько желаемых свойств:
- каждый класс предков появляется ровно один раз
- класс всегда появляется перед его предком ( "монотонность" )
- прямые родители того же класса должны отображаться в том же порядке, в каком они указаны в определении класса ( "согласованный локальный порядок приоритета" )
- если дети класса
A
всегда появляются перед дочерними элементами класса B
, тогда A
должен появиться перед B
( "согласованный расширенный порядок приоритета" )
С вашим кодом второе ограничение требует, чтобы Enemy
появился первым; третье ограничение требует, чтобы Player
появился первым. Поскольку нет способа удовлетворить все ограничения, отчеты python о том, что ваша иерархия наследования незаконна.
Ваш код будет работать, если вы переключите порядок базовых классов в GameObject
следующим образом:
class GameObject(Enemy, Player):
pass
Это не просто техническая деталь. В некоторых (надеюсь, редких) случаях вам может понадобиться подумать о том, какой класс следует использовать для захвата метода, который вы вызывали, если метод определен в нескольких классах. Порядок, в котором вы определяете базовые классы, влияет на этот выбор.
Ответ 3
То, что вы написали, - это то, что вы хотите, чтобы GameObject
был как Player
, так и Enemy
. Но Enemy
уже есть Player
. Проблема MRO гласит, что если бы у вас было поле a
в Player
, запрос этого поля в экземпляре GameObject
был бы неоднозначным: должен ли он быть a
от первого Player
, который вы наследуете, или один из Player
, который вы наследуете через наследование Enemy
?
Но вы уверены, что не хотите использовать композицию вместо наследования?
class GameObject(object):
def __init__(self):
self.player = Player()
self.enemy = Enemy()