2010-09-28 14 views

Odpowiedz

8

super() będzie zawsze tylko rozwiązać jeden typ klasy dla danej metody, więc jeśli dziedziczenie z wielu klas i chce wywołać metodę w obu z nich, ty” Będę musiał to zrobić jawnie.

+0

Dzięki za odpowiedź. Teraz nie czuję się już brudny, ponieważ nie używam super(). – sayap

+4

Podczas gdy jawne wywoływanie metod klasy podstawowej * może * działać dla bardzo prostych scenariuszy, takich jak przykład osoby pytającej, ulegnie ona rozbiciu, jeśli klasy podstawowe same odziedziczą ze wspólnej podstawy i nie chcesz, aby metoda najwyższej klasy bazy była wywoływana dwa razy . Jest to znane jako "dziedziczenie diamentów" i jest to duży problem dla wielu systemów dziedziczenia (jak w C++). Wspólne dziedziczenie Pythona (z 'super()') pozwala w wielu przypadkach łatwo go rozwiązać (choć nie oznacza to, że hierarchia dziedziczenia wielu spółdzielni jest łatwa do zaprojektowania lub zawsze jest dobrym pomysłem). – Blckknght

4

Jeśli dodano następujące:

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() 

Następnie Super (C, self) .foo() odnosi się do A.foo

Wyjście jest

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

[Edytuj: dołącz dodatkowe informacje i linki]

+0

Tak, to było moje pytanie. Więc super nie jest przeznaczone dla tej sprawy? – sayap

+0

@sayap: Mam edytowane moją odpowiedź na to linki, które są istotne dla zrozumienia, jak fajne utwory. Działa, ale najpierw znajdzie A.foo, a jeśli nie było A.foo, to B.foo zostałby sprawdzony. – pyfunc

+0

Dlaczego chciałbym mieć wywołanie A.foo() dwa razy? – sayap

6

Super wywoła metodę foo w "pierwszej" superklasie. Jest to oparte na metodzie Resolution Order Order (__mro__) dla klasy C.

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

Dlatego jeśli zadzwonisz super(C, self).foo(), A.foo nazywa. Jeśli zmienisz porządek dziedziczenia na class C(B, A):, zostanie to odwrócone. __mro__ teraz wygląda:

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

Jeśli zadzwonisz super(C, self).foo() po dokonaniu tej zmiany, B.foo() zostanie sprawdzony.

+0

yay dla introspekcji –

19

super jest rzeczywiście przeznaczony do tej sytuacji, ale działa tylko wtedy, gdy używasz go konsekwentnie. Jeśli klasy bazowe nie wszystkie również używają super, to nie zadziała i chyba że metoda jest w object musisz użyć czegoś podobnego do zwykłej klasy bazowej, aby zakończyć połączenia 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 pyta, dlaczego nie musi być wspólna podstawa:

Bez FooBase który implementuje foo ale nie wywołuje super() ostatnia klasa, która ma wywołać super() dostanie błąd atrybutu jak tam nie ma podstawowa metoda wywoływania.

Gdyby nie było odrębnych klas bazowych class A(AFooBase): i class B(BFooBase): wywołanie w Asuper() nazwałbym Sposób AFooBase i metody w B nigdy nie zostanie wywołana. Gdy podstawa jest wspólna dla wszystkich klas, przechodzi do końca kolejności rozwiązywania metod i możesz mieć pewność, że niezależnie od tego, jak zdefiniowane zostaną klasy, metoda klasy bazowej będzie ostatnią wywołaną.

+0

myślę, że to jest o wiele bardziej pouczające niż jedną odpowiedź zaakceptowana. Czy mógłbyś rozwinąć, dlaczego potrzebujesz wspólnej klasy bazowej, aby to zadziałało? – Marcin

+0

@marcin Ty nie musisz potrzebujemy wspólnej podstawy, aby super() pracę. @duncan demonstruje, w jaki sposób super() jest jeszcze lepszy, gdy mamy do czynienia z dziedziczeniem diamentów. A tak na marginesie, wyjście wywołania 'C(). Foo()' będzie 'B.foo() A.foo(). C.foo() 'zamiast" A .. B .. C .. ", a powodem jest wyjaśnienie MRO w [Odpowiedź Manoj'a] (http://stackoverflow.com/a/3810465/728675) – RayLuo

+0

@RayLuo Ta odpowiedź dosłownie mówi, że wspólna baza jest wymagana. – Marcin

0

Jest to możliwe dzięki 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

Więc co zyskasz nazywając 'super (C, self) .foo()' lub 'super (A, self) .foo()' zamiast 'A.foo()' lub 'B.foo()' ? – FooF

1

Jak powiedział Duncan powyżej, można uzyskać przewidywalne wyniki, zwłaszcza jeśli używasz super() konsekwentnie.

Wyniki poniższego testu były pomocne dla mnie:

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()' 

ten wypisze:

>>> 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

Dzięki dla wszystkich, którzy przyczynili się do tego wątku. Podsumowując:

  • The (currently) accepted answer jest niedokładna. Prawidłowy opis powinien być: super() NIE jest TYLKO dobry do rozwiązywania pojedynczego dziedziczenia, ALE RÓWNIEŻ wiele dziedziczenia. A powód jest dobrze wyjaśnione w komentarzu @blckknght „s:

    Podczas jawne wywołanie metody klasy bazowej może pracować na bardzo prostych sytuacjach, takich jak np pytający użytkownika, będzie ona rozbić jeśli zajęcia same bazowe dziedziczą z powszechną podstawa i nie chcesz, aby metoda najwyższej klasy bazowej była wywoływana dwa razy. Jest to znane jako "dziedziczenie diamentów" i jest to duży problem dla wielu systemów dziedziczenia (jak w C++). Wspólne dziedziczenie Pythona (z super()) pozwala w wielu przypadkach łatwo go rozwiązać (choć nie oznacza to, że hierarchia dziedziczenia wielu spółdzielni jest łatwa do zaprojektowania lub zawsze jest dobrym pomysłem).

  • Właściwy sposób, jako @duncan pointed out, to użycie super(), ale używaj go konsekwentnie.

    super jest rzeczywiście przeznaczony do tej sytuacji, ale działa tylko wtedy, gdy używasz go konsekwentnie. Jeśli klasy bazowe nie wszystkie również używają super, to nie zadziała i chyba że metoda jest w object musisz użyć czegoś podobnego do zwykłej klasy bazowej, aby zakończyć połączenia 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 
    

    Ale warto też podkreślić, że metoda kolejność wywoływania może nie wydawać intuicyjne. Rezultat:

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

    Powód tego pozornie dziwnego porządku wynika z MRO. As @Manoj-Govindan pointed out,

    super() wywoła metodę foo na "pierwszej" superklasie. Jest to oparte na metodzie Resolution Order Order (__mro__) dla klasy C.

    >>> C.__mro__ 
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
    >>> 
    
  • Jako zasada, jeśli chcesz podróżować z powrotem do wszystkich metod dominujących, ale tak naprawdę nie obchodzi kolejność invoke, użyj super() consisently. W przeciwnym razie możesz jawnie wywołać metody nadrzędne w określonej kolejności.

  • Nie mieszać użycie super() i wyraźnego powołania chociaż. W przeciwnym razie skończy się to nieprzyjemnym duplikowaniem, o którym mowa w this answer.

PS: Przejrzałem większość wspomnianych źródeł.

Powiązane problemy