python 'super' в множественном наследовании - неожиданный вывод кода - PullRequest
2 голосов
/ 08 мая 2019

Я относительно новичок в ООП Python. Хотя у меня есть некоторый опыт работы с JAVA OOP и я знаю значение метода «super», я изо всех сил пытаюсь понять, как он работает в python, когда у меня есть несколько наследований (которых нет в JAVA)

После того, как я покопался, чтобы найти ответы, я прочитал, что в соответствии с графиком наследования для каждого класса Python создал Порядок разрешения методов (MRO), чтобы определить, в каком порядке искать экземпляр метода.

Я также читал, что MRO определяется "старым стилем" или "новым стилем", в зависимости от моей версии Python. У меня есть Python 3.7, поэтому я использую метод "новый стиль".

Если я правильно понимаю, каждый раз, когда я перезаписываю метод и вызываю 'super', 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 (в соответствии с «новым стилем»)!

1 Ответ

1 голос
/ 08 мая 2019

Проблема в том, что ваши классы не определены совместно. И A, и B считают, что в нем введено foo, и поэтому нет необходимости вызывать super().foo, потому что каждый думает, что он будет последним классом в любом потенциальном MRO для определения foo. AB доказывает, что это не так.

Когда вы звоните AB.foo, первое, что он делает, это звонит A.foo. Однако A.foo не использует super, поэтому цепочка заканчивается, и B.foo никогда не вызывается.

В правильно спроектированной иерархии ровно один класс «вводит» метод и отвечает за завершение цепочки, не вызывая super. Любой другой класс, который хочет быть частью иерархии, отвечает за вызов super.

В вашем A / B / AB случае у вас есть несколько вариантов:

  1. Имейте, что и A, и B наследуются от FooBase, и каждый из них вызывает super().foo() из своей реализации foo. FooBase.foo сама не вызывает super.

  2. Вместо 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__ работать должным образом в условиях совместного наследования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...