2010-09-28 1 views
13

С помощью следующего примера кода можно использовать super, или C должен позвонить A.foo и B.foo явно?python множественное наследование с разных путей с таким же именем метода

class A(object): 
    def foo(self): 
     print 'A.foo()' 

class B(object): 
    def foo(self): 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     print 'C.foo()' 
     A.foo(self) 
     B.foo(self) 

ответ

8

super() только когда-либо решить один тип класса для данного метода, так что если вы унаследовав от нескольких классов и хотите вызвать метод в обоих из них, вы» нужно сделать это явно.

+0

Спасибо за ответ. Теперь я больше не чувствую себя грязным, потому что не использовал super(). – sayap

+4

При явном вызове методов базового класса * может * работать для очень простых сценариев, таких как пример опроса, он сломается, если базовые классы сами наследуют от общей базы, и вы не хотите, чтобы метод конечного базового класса вызывался дважды , Это известно как «наследование бриллиантов», и это большая проблема для многих множественных систем наследования (например, на C++). Совместное многократное наследование Python (с помощью 'super()') позволяет легко решить его во многих случаях (хотя это не означает, что совместная иерархия множественного наследования проста в дизайне или всегда хорошая идея). – Blckknght

4

Если вы добавили следующее:

class A(object): 
    def foo(self): 
     print 'A.foo()' 

class B(object): 
    def foo(self): 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     super(C, self).foo() 
     print 'C.foo()' 
     A.foo(self) 
     B.foo(self) 


c = C() 
c.foo() 

Тогда супер (C, самость) .foo() относится к A.foo

Выход

A.foo() 
C.foo() 
A.foo() 
B.foo() 

[Изменить: Включить дополнительную информацию и ссылки]

+0

Да, это был мой вопрос. Так супер не предназначен для этого случая? – sayap

+0

@sayap: Я отредактировал свой ответ, чтобы включить ссылки, которые имеют отношение к пониманию того, как работает супер. Он работает, но он сначала найдет A.foo, и если A.foo не будет там, где B.foo будет рассмотрен. – pyfunc

+0

Почему я хочу, чтобы A.foo() вызывал дважды? – sayap

6

Super вызовет метод foo на «первом» суперклассе. Это основано на порядке разрешения метода (__mro__) для класса C.

>>> C.__mro__ 
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
>>> 

Поэтому, если вы звоните super(C, self).foo(), A.foo называется. Если вы измените порядок наследования до class C(B, A):, то это будет отменено. __mro__ теперь выглядит следующим образом:

>>> C.__mro__ 
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>) 
>>> 

Если вы звоните super(C, self).foo() после внесения этого изменения, B.foo() будет вызван.

+0

yay для интроспекции –

19

super действительно предназначен для этой ситуации, но он работает только в том случае, если вы используете его последовательно. Если базовые классы не все также используют super, это не сработает, и если метод не находится в object, вы должны использовать что-то вроде общего базового класса для завершения цепочки вызовов super.

class FooBase(object): 
    def foo(self): pass 

class A(FooBase): 
    def foo(self): 
     super(A, self).foo() 
     print 'A.foo()' 

class B(FooBase): 
    def foo(self): 
     super(B, self).foo() 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     super(C, self).foo() 
     print 'C.foo()' 

@Marcin спрашивает, почему там должно быть общее основание:

Без FooBase, который реализует foo но не называют super() последний класс, который действительно требует super() получите ошибку атрибута как нет базовый метод для вызова.

Если были отдельные базовые классы class A(AFooBase): и class B(BFooBase):super() вызов в A бы вызвать метод в AFooBase и метод в B никогда не будет вызван. Когда база является общей для всех классов, она подходит к концу порядка разрешения метода, и вы можете быть уверены, что независимо от того, как будут определены классы, метод базового класса будет последним.

+0

Я думаю, что это более информативный ответ, чем тот, который был принят. Не могли бы вы рассказать о том, зачем нужен общий базовый класс для выполнения этой работы? – Marcin

+0

@marcin Вам не нужна общая база для работы super(). @duncan просто демонстрирует, как super() еще лучше справляется с алмазным наследованием. И, кстати, вывод вызова 'C(). Foo()' будет 'B.foo() A.foo(). C.foo() ', а не« A .. B .. C .. », и причина объясняется MRO в [ответе Маной] (http://stackoverflow.com/a/3810465/728675) – RayLuo

+0

@RayLuo Этот ответ буквально говорит, что требуется общая база – Marcin

0

Это возможно с super

class A(object): 
    def foo(self): 
     print 'A.foo()' 

class B(object): 
    def foo(self): 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     print 'C.foo()' 
     super(C, self).foo() # calls A.foo() 
     super(A, self).foo() # calls B.foo() 
+0

Итак, что вы получаете, вызывая 'super (C, self) .foo()' или 'super (A, self) .foo()' вместо 'A.foo()' или 'B.foo()' ? – FooF

1

Как было сказано выше Дункан, вы можете получить предсказуемые результаты, особенно если вы используете супер() последовательно.

Результаты следующего теста были полезны для меня:

class FooBase(object): 
    def foo(self): 
     print 'FooBase.foo()' 

class A(FooBase): 
    def foo(self): 
     print 'A.foo() before super()' 
     super(A, self).foo() 
     print 'A.foo() after super()' 

class B(FooBase): 
    def foo(self): 
     print 'B.foo() before super()' 
     super(B, self).foo() 
     print 'B.foo() after super()' 

class C(A, B): 
    def foo(self): 
     print 'C.foo() before super()' 
     super(C, self).foo() 
     print 'C.foo() after super()' 

Это напечатает:

>>> c = C() 
>>> c.foo() 
C.foo() before super() 
A.foo() before super() 
B.foo() before super() 
FooBase.foo() 
B.foo() after super() 
A.foo() after super() 
C.foo() after super() 
7

Спасибо за всех тех, кто способствовал этой теме. Подводя итог:

  • The (currently) accepted answer неточен. Правильное описание должно быть: super() НЕ ТОЛЬКО хорошо подходит для разрешения одиночного наследования, НО ТАКЖЕ множественное наследование. И причина хорошо объясняется в @blckknght «s комментарий:

    Хотя явного вызова методов базового класса могут работать на очень простых сценариев, таких как, например вопрошающего, он разрушит, если сами базовые классы наследуют от общей и вы не хотите, чтобы метод основного базового класса вызывался дважды. Это известно как «наследование бриллиантов», и это большая проблема для многих множественных систем наследования (например, на C++). Совместное множественное наследование Python (с супер()) позволяет вам легко решить его во многих случаях (хотя это не означает, что совместная иерархия множественного наследования проста в дизайне или всегда хорошая идея).

  • Правильный способ, как @duncan pointed out, использовать super(), но использовать его последовательно.

    super действительно предназначен для этой ситуации, но он работает только в том случае, если вы используете его последовательно. Если базовые классы не все также используют super, это не сработает, и если метод не находится в object, вы должны использовать что-то вроде общего базового класса для завершения цепочки вызовов super.

    class FooBase(object): 
        def foo(self): pass 
    
    class A(FooBase): 
        def foo(self): 
         super(A, self).foo() 
         print 'A.foo()' 
    
    class B(FooBase): 
        def foo(self): 
         super(B, self).foo() 
         print 'B.foo()' 
    
    class C(A, B): 
        def foo(self): 
         super(C, self).foo() 
         print 'C.foo()' 
    
    C().foo() # Run this 
    

    Но также стоит отметить, что метод порядок вызова может показаться не интуитивно. Результат:

    B.foo() 
    A.foo() 
    C.foo() 
    

    И причина этого, казалось бы, странного порядка основана на MRO. As @Manoj-Govindan pointed out,

    super() будет вызывать метод Foo на «первый» супер классе. Это основано на порядке разрешения метода (__mro__) для класса C.

    >>> C.__mro__ 
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
    >>> 
    
  • Как правило, если вы хотите путешествовать на все родительские методы, но на самом деле не волнует порядок Invoke, используйте super() consisently. В противном случае вы можете явно указать родительские методы в определенном порядке.

  • Не смешивайте использование супер() и явного вызова. В противном случае вы столкнетесь с неприятным дублированием, как упомянуто в this answer.

PS: Я поддержал большинство источников, упомянутых выше.