Я относительно новичок в python OOP. Хотя у меня есть некоторый опыт работы с JAVA OOP, и я знаю значение метода «супер», я изо всех сил пытаюсь понять, как он работает в python, когда у меня есть множественное наследование (которого нет в JAVA)
Покопавшись в поисках ответов, я прочитал, что, согласно графику наследования, для каждого класса python создал порядок разрешения методов (MRO), чтобы определить, в каком порядке искать метод экземпляра.
Я также читал, что MRO определяется «старым стилем» или «новым стилем», в зависимости от моей версии Python. У меня python 3.7, поэтому я использую метод «новый стиль».
Если я правильно понимаю, каждый раз, когда я переопределяю метод и вызываю «супер», python переходит к методу в классе, который отображается после текущего класса в MRO.
Действительно, когда я запускаю следующий код:
class A(object):
def go(self):
print("go A go!")
class B(A):
def go(self):
super(B, self).go()
print("go B go!")
class C(A):
def go(self):
super(C, self).go()
print("go C go!")
class D(B,C):
def go(self):
super(D, self).go()
print("go D go!")
if __name__ == "__main__":
d = D()
d.go()
print(D.__mro__)
Я получил вывод:
go A go!
go C go!
go B go!
go D go!
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Пока все хорошо, но когда я попытался запустить следующий код:
class A(object):
def foo(self):
print('this is A')
class B(object):
def foo(self):
print('this is B')
class AB(A,B):
def foo(self):
super(AB,self).foo()
print('this is AB')
if __name__ == '__main__':
x = AB()
x.foo()
print(AB.__mro__)
Я получил вывод:
this is A
this is AB
(<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
Вместо вывода, как я ожидал:
this is B
this is A
this is AB
(<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
Так что, видимо, я не понимаю, что происходит...
Будем очень признательны за любое объяснение этой ситуации, а также о том, как именно python определяет MRO (по «новому стилю»)!
Я это уже читал... не нашел ответа на свою проблему...
Вы читали статью Гвидо ван Россума о MRO?: python-history.blogspot.com/2010/06/…
Также прочитайте Python's super()
считается супер!. Краткий ответ: в иерархии кооперативного наследования именно классу один разрешено определять foo
, который не вызывает super().foo()
. У вас есть два: A
и B
. Один класс должен быть «базовым», который будет отображаться последним в MRO любого подкласса, наследуемого от класса, обеспечивающего foo
.
Проблема в том, что ваши классы не определены совместно. И A
, и B
думают, что он ввел foo
, и поэтому нет необходимости вызывать super().foo
, потому что каждый думает, что это будет последний класс в любом потенциальном MRO для определения foo
. AB
доказывает, что это не так.
Когда вы звоните AB.foo
, он первым делом звонит A.foo
. Однако A.foo
не использует super
, поэтому цепочка заканчивается, а B.foo
никогда не вызывается.
В правильно спроектированной иерархии именно класс один «вводит» метод и отвечает за завершение цепочки, не вызывая super
. Любой другой класс, который хочет быть частью иерархии, отвечает за вызов super
.
В вашем случае A
/B
/AB
у вас есть несколько вариантов:
Пусть и A
, и B
наследуются от FooBase
, и каждый из них вызывает super().foo()
из своей реализации foo
. FooBase.foo
сам не звонит super
.
Вместо того, чтобы AB
наследоваться напрямую от A
и B
, пусть он наследуется от оберток вокруг одного или обоих из них, где обертка правильно реализует совместное наследование. (Подробнее см. Super() в Python считается супер!..)
Например, здесь мы обертываем A
и пусть B
выступает в качестве основы для foo
:
class A:
def foo(self):
print('this is A')
class AWrapper:
def __init__(self, **kwargs):
super().__init__()
self.a = A()
def foo(self):
super().foo()
self.a.foo()
class B(object):
def foo(self):
print('this is B')
# Important: AWrapper must come first
class AB(AWrapper, B):
def foo(self):
super().foo()
print('this is AB')
AB().foo()
Обратите внимание, что это усложняется, если само __init__
необходимо определить в A
и B
; см. связанную статью для получения дополнительных советов о том, как заставить __init__
работать правильно в условиях совместного наследования.
Возможный дубликат Как Python super() работает с множественным наследованием?