Co robi „super” w Pythonie?

563

Jaka jest różnica pomiędzy:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

i:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Widziałem, że jestem superczęsto używany na zajęciach z jednym dziedzictwem. Rozumiem, dlaczego miałbyś używać go w wielokrotnym dziedzictwie, ale nie jestem pewien, jakie są zalety korzystania z tego w takiej sytuacji.

użytkownik25785
źródło

Odpowiedzi:

308

Korzyści z super()pojedynczego dziedziczenia są minimalne - przeważnie nie trzeba na stałe wpisywać nazwy klasy podstawowej do każdej metody, która korzysta z jej metod nadrzędnych.

Bez niego jednak prawie niemożliwe jest wielokrotne dziedziczenie super(). Obejmuje to popularne idiomy, takie jak mixiny, interfejsy, klasy abstrakcyjne itp. Dotyczy to również kodu, który później rozszerza twój. Jeśli ktoś później chciałby napisać klasę rozszerzoną Childi mikser, ich kod nie działałby poprawnie.

John Millikin
źródło
6
czy możesz podać przykład, co masz na myśli mówiąc, że „nie działałoby to poprawnie”?
Charlie Parker
319

Co za różnica?

SomeBaseClass.__init__(self) 

środki zadzwonić SomeBaseClass„s __init__. podczas

super(Child, self).__init__()

oznacza wywołanie powiązania __init__z klasy nadrzędnej, która następuje Childw kolejności rozstrzygania metod w instancji (MRO).

Jeśli instancja jest podklasą potomną, w MRO może znajdować się inny rodzic.

Wyjaśnione po prostu

Kiedy piszesz klasę, chcesz, aby inne klasy mogły z niej korzystać. super()ułatwia innym klasom korzystanie z klasy, którą piszesz.

Jak mówi Bob Martin, dobra architektura pozwala na jak najdłuższe opóźnienie podjęcia decyzji.

super() może włączyć tego rodzaju architekturę.

Gdy inna klasa podklasuje napisaną przez ciebie klasę, może ona również dziedziczyć po innych klasach. I te klasy mogą mieć __init__następującą po tym __init__na podstawie kolejności klas do rozpoznawania metod.

Bez superciebie prawdopodobnie kodowałbyś na stałe rodzica klasy, którą piszesz (tak jak w przykładzie). Oznaczałoby to, że nie zadzwonisz do następnego __init__w MRO, a zatem nie będziesz mógł ponownie użyć kodu w nim.

Jeśli piszesz własny kod do użytku osobistego, możesz nie dbać o to rozróżnienie. Ale jeśli chcesz, aby inni używali Twojego kodu, używanie superto jedna z rzeczy, która umożliwia większą elastyczność użytkownikom kodu.

Python 2 kontra 3

Działa to w Python 2 i 3:

super(Child, self).__init__()

Działa to tylko w Pythonie 3:

super().__init__()

Działa bez argumentów, przesuwając się w górę w ramce stosu i pobierając pierwszy argument do metody (zwykle selfdla metody instancji lub clsmetody klasy - ale mogą to być inne nazwy) i znajdując klasę (np. Child) W wolnych zmiennych ( __class__w metodzie jest sprawdzana nazwa jako zmienna o swobodnym zamknięciu).

Wolę zademonstrować kompatybilność krzyżową super, ale jeśli używasz tylko Pythona 3, możesz wywołać go bez argumentów.

Pośrednictwo z kompatybilnością do przodu

Co ci to daje? W przypadku pojedynczego dziedziczenia przykłady z pytania są praktycznie identyczne z punktu widzenia analizy statycznej. Jednak użycie superdaje warstwę pośrednią z kompatybilnością do przodu.

Kompatybilność z przyszłością jest bardzo ważna dla doświadczonych programistów. Chcesz, aby Twój kod działał przy minimalnych zmianach podczas jego zmiany. Patrząc na historię zmian, chcesz dokładnie zobaczyć, co się zmieniło, kiedy.

Możesz zacząć od pojedynczego dziedziczenia, ale jeśli zdecydujesz się dodać kolejną klasę podstawową, musisz tylko zmienić linię z podstawami - jeśli podstawy zmienią się w klasie, którą dziedziczysz (powiedzmy, że dodany jest mixin), zmienisz nic w tej klasie. Zwłaszcza w Pythonie 2 uzyskanie superpoprawnych argumentów i poprawnych argumentów metody może być trudne. Jeśli wiesz, że używasz superpoprawnie z pojedynczym dziedziczeniem, to sprawia, że ​​debugowanie jest trudniejsze.

Wstrzykiwanie zależności

Inne osoby mogą używać Twojego kodu i wprowadzać rodziców do rozwiązania metody:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

Załóżmy, że dodajesz kolejną klasę do swojego obiektu i chcesz wstrzyknąć klasę między Foo a Bar (do testowania lub z innego powodu):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

Użycie un-super potomka nie wstrzykuje zależności, ponieważ używane dziecko na stałe zakodowało metodę, która ma zostać wywołana po swojej:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

Jednak klasa z dzieckiem, które używa, supermoże poprawnie wstrzyknąć zależność:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Adresowanie komentarza

Dlaczego na świecie miałoby to być przydatne?

Python linearyzuje skomplikowane drzewo dziedziczenia za pomocą algorytmu linearyzacji C3 w celu utworzenia metody rozstrzygania metod (MRO).

Chcemy, aby metody były wyszukiwane w tej kolejności .

Aby metoda zdefiniowana w obiekcie nadrzędnym mogła znaleźć następną w tej kolejności bez super, musiałaby to zrobić

  1. pobierz mro z typu instancji
  2. poszukaj typu, który definiuje metodę
  3. znajdź następny typ za pomocą metody
  4. powiąż tę metodę i wywołaj ją z oczekiwanymi argumentami

Nie UnsuperChildpowinien mieć dostępu do InjectMe. Dlaczego wniosek „Zawsze unikaj używania super”? Czego tu brakuje?

UnsuperChildMa nie mieć dostępu do InjectMe. To on UnsuperInjectorma dostęp do InjectMe- a jednak nie może wywołać metody tej klasy z metody, którą dziedziczy UnsuperChild.

Obie klasy podrzędne zamierzają wywoływać metodę o tej samej nazwie, która pojawi się następnie w MRO, która może być inną klasą, o której nie wiedziała, kiedy została utworzona.

Ta, która nie superstosuje sztywnych kodów metody rodzica - w ten sposób ograniczyła zachowanie swojej metody, a podklasy nie mogą wprowadzać funkcjonalności do łańcucha połączeń.

Ten o super większej elastyczności. Łańcuch wywołań metod może zostać przechwycony, a funkcjonalność wprowadzona.

Możesz nie potrzebować tej funkcji, ale mogą to zrobić podklasenci twojego kodu.

Wniosek

Zawsze używaj, superaby odwoływać się do klasy nadrzędnej zamiast na stałe ją kodować.

Zamierzamy odwoływać się do klasy nadrzędnej, która jest następna w linii, a nie konkretnie tej, z której dziedziczy dziecko.

Nieużywanie supermoże powodować niepotrzebne ograniczenia dla użytkowników Twojego kodu.

Aaron Hall
źródło
W C, DI jak to . kod jest tutaj . Jeśli dodam jeszcze jedną implementację listinterfejsu, powiedzmy, że doublylinkedlistaplikacja płynnie go wybiera. Mogę uczynić mój przykład bardziej konfigurowalnym, wprowadzając config.txti implementując łącze w czasie ładowania. Czy to dobry przykład? Jeśli tak, jak powiązać kod? Zobacz pierwszy adv DI na wiki. Gdzie można skonfigurować jakąkolwiek nową implementację? w kodzie
przeeksponuj
Nowa implementacja jest tworzona przez dziedziczenie, na przykład, gdy jedna z klas „Injector” dziedziczy po InjectMeklasie. Komentarze nie są jednak przeznaczone do dyskusji, więc sugeruję, aby omówić to dalej z innymi na czacie lub zadać nowe pytanie na głównej stronie.
Aaron Hall
świetna odpowiedź! ale w przypadku wielokrotnego dziedziczenia występują komplikacje związane z __init__funkcją super () i funkcjami. szczególnie jeśli podpis __init__różni się w zależności od klasy w hierarchii. Dodałem odpowiedź, która koncentruje się na tym aspekcie
Aviad Rozenhek
35

Grałem trochę super()i zdałem sobie sprawę, że możemy zmienić kolejność połączeń.

Na przykład mamy następną strukturę hierarchiczną:

    A
   / \
  B   C
   \ /
    D

W tym przypadku MRO z D będzie (tylko dla Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Stwórzmy klasę, w której super()wywołania będą wykonywane po wykonaniu metody.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

Widzimy więc, że kolejność rozdzielczości jest taka sama jak w MRO. Ale kiedy wywołujemy super()na początku metody:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

Mamy inną kolejność, jest odwrócona kolejność krotki MRO.

    A
   / 
  B  C
    /
    D 

Do lektury polecam kolejne odpowiedzi:

  1. Przykład linearyzacji C3 z super (duża hierarchia)
  2. Ważne zmiany zachowania między klasami starego i nowego stylu
  3. The Inside Story on New-Class Classes
SKhalymon
źródło
Nie rozumiem, dlaczego zmienia się kolejność. W pierwszej części rozumiem, że DBCA, ponieważ D jest pierwszą klasą, a następnie po załadowaniu self (B, C) ostatecznie wydrukuje B, C, a następnie tylko A, ponieważ B (A), C (A) wskazał na siebie dla finału część. Jeśli podążę za tym rozumowaniem, to czy druga część nie powinna być jak BCAD? Proszę, proszę, proszę wytłumacz mi trochę.
JJson
Moje zło, nie zauważyłem, że każda instancja każdej klasy została najpierw zainicjowana przez super (). Jeśli tak, to czy nie powinien to być ABCD? W jakiś sposób rozumiem, jak doszło do ACBD, ale nadal nie byłem w stanie przekonać i nadal mam trochę zamieszania. rozumiem, że d = D () wywołuje klasę D (B, C) z 2 parametrami własnymi, ponieważ super () jest inicjowana jako pierwsza, następnie B jest wywoływana razem z jej atrybutami, a następnie D nie jest drukowane przed C, ponieważ klasa D (B, C) zawiera 2 autoparametry, więc musi wykonać drugi, którym jest klasa C (A), po wykonaniu nie ma już więcej parametrów do wykonania
JJson
Będzie oparty na definicji mro .
SKhalymon
1
następnie wydrukuje C, następnie wydrukuje B, a na końcu wydrukuje D. Czy mam rację?
JJson
2
Bardzo łatwo jest zrozumieć drugi, o ile dostaniesz pierwszy. To jest jak stos. wpychasz print '' do stosu i robisz super (), kiedy skończysz A, zaczyna drukować rzeczy na tym stosie, więc kolejność jest odwrotna.
przyznają niedz.
35

Czy to wszystko nie zakłada, że ​​klasa podstawowa jest klasą w nowym stylu?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

Nie działa w Pythonie 2. class Amusi być w nowym stylu, tj .:class A(object)

Mahawke
źródło
20

Podczas wywoływania super()rozwiązania z wersją nadrzędną metody klasy, metody instancji lub metody statycznej chcemy przekazać bieżącą klasę, w której zakresie jesteśmy pierwszym argumentem, aby wskazać zakres nadrzędny, który próbujemy rozwiązać, i jako drugi argument będący przedmiotem zainteresowania wskazujący, do którego obiektu próbujemy zastosować ten zakres.

Rozważmy hierarchii klasa A, Bi Cgdzie każda klasa jest rodzicem jednego po nim, i a, bi codpowiednie instancje każdy.

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

Korzystanie superz metody statycznej

np. korzystanie super()z __new__()metody

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

Wyjaśnienie:

1 - chociaż zwykle __new__()przyjmuje się jako pierwszy param odniesienie do klasy wywołującej, nie jest on zaimplementowany w Pythonie jako metoda klasy, ale raczej metoda statyczna. Oznacza to, że odwołanie do klasy musi zostać jawnie przekazane jako pierwszy argument podczas __new__()bezpośredniego wywoływania :

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2- podczas wywoływania, super()aby dostać się do klasy nadrzędnej, przekazujemy klasę potomną Ajako pierwszy argument, a następnie przekazujemy odniesienie do obiektu będącego przedmiotem zainteresowania, w tym przypadku jest to odwołanie do klasy, które zostało przekazane, gdy A.__new__(cls)zostało wywołane. W większości przypadków jest to również odniesienie do klasy podrzędnej. W niektórych sytuacjach może tak nie być, na przykład w przypadku dziedziczenia wielu generacji.

super(A, cls)

3 - ponieważ ogólną zasadą __new__()jest metoda statyczna, super(A, cls).__new__zwróci również metodę statyczną i w tym przypadku należy podać jawnie wszystkie argumenty, w tym odniesienie do obiektu zainteresowania cls.

super(A, cls).__new__(cls, *a, **kw)

4- robienie tego samego bez super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

Korzystanie superz metody instancji

np. korzystanie super()z wewnątrz__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

Wyjaśnienie:

1- __init__to metoda instancji, co oznacza, że ​​jako pierwszy argument przyjmuje odwołanie do instancji. Po wywołaniu bezpośrednio z instancji odwołanie jest przekazywane niejawnie, co oznacza, że ​​nie trzeba go określać:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2- podczas wywoływania super()wewnątrz __init__()przekazujemy klasę potomną jako pierwszy argument, a przedmiot zainteresowania jako drugi argument, który ogólnie jest odniesieniem do instancji klasy potomnej.

super(A, self)

3- Wywołanie super(A, self)zwraca serwer proxy, który rozwiąże zakres i zastosuje go selftak, jakby był teraz instancją klasy nadrzędnej. Nazwijmy to proxy s. Ponieważ __init__()jest to metoda instancji, wywołanie s.__init__(...)domyślnie przekaże referencję selfjako pierwszy argument do rodzica __init__().

4- aby zrobić to samo bez supermusimy przekazać odwołanie do instancji jawnie do wersji rodzica __init__().

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

Używanie supermetody klasowej

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

Wyjaśnienie:

1- Metodę klasy można wywołać bezpośrednio z klasy i przyjmuje jako swój pierwszy parametr odwołanie do klasy.

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

2- podczas wywoływania super()w ramach metody klasy w celu rozstrzygnięcia jej wersji nadrzędnej, chcemy przekazać bieżącą klasę podrzędną jako pierwszy argument wskazujący zakres nadrzędny, którego próbujemy rozstrzygnąć, oraz obiekt będący przedmiotem zainteresowania jako drugi argument do wskazania, do którego obiektu chcemy zastosować ten zakres, co ogólnie jest odniesieniem do samej klasy potomnej lub jednej z jej podklas.

super(B, cls_or_subcls)

3- Połączenie super(B, cls)jest rozpatrywane w zakresie Ai dotyczy go cls. Ponieważ alternate_constructor()jest to metoda klasy, wywołanie super(B, cls).alternate_constructor(...)domyślnie przekaże referencję clsjako pierwszy argument do Awersjialternate_constructor()

super(B, cls).alternate_constructor()

4- aby zrobić to samo bez użycia super(), musisz uzyskać odniesienie do niezwiązanej wersji A.alternate_constructor()(tj. Jawnej wersji funkcji). Po prostu zrobienie tego nie zadziałałoby:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

Powyższe nie zadziałałoby, ponieważ A.alternate_constructor()metoda przyjmuje Ajako pierwsze odwołanie niejawne odniesienie . clsPrzepuszcza tutaj będzie jego drugi argument.

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)
Michael Ekoka
źródło
6

Wiele świetnych odpowiedzi, ale dla osób uczących się wizualnie: Po pierwsze, poznajmy argumenty do super, a potem bez. przykład drzewa super dziedziczenia

Wyobraź sobie, że istnieje instancja jackutworzona z klasy Jack, która ma łańcuch dziedziczenia pokazany na zielono na zdjęciu. Powołanie:

super(Jack, jack).method(...)

użyje MRO (Method Resolution Order) jack(jego drzewa dziedziczenia w określonej kolejności) i rozpocznie wyszukiwanie Jack. Dlaczego można zapewnić klasę nadrzędną? Cóż, jeśli zaczniemy wyszukiwanie od instancji jack, znajdzie metodę instancji, chodzi o to, aby znaleźć metodę nadrzędną.

Jeśli nie podaje się argumentów super, to tak jak pierwszy przekazany argument jest klasą self, a drugim przekazanym argumentem jest self. Są one automatycznie obliczane dla Ciebie w Python3.

Jednak powiedzmy, że nie chcemy używać Jackmetody, zamiast przekazywać Jack, moglibyśmy przejść, Jenaby zacząć szukać metody od Jen.

Przeszukuje jedną warstwę na raz (szerokość nie głębokość), na przykład gdy Adami Sueoba mają odpowiednią metodą, a ten z Sueznajdzie się w pierwszej kolejności.

Jeśli Caini Sueoba miały wymaganą metodę, Cainjest metoda, która zostanie nazwana pierwszą. Odpowiada to w kodzie:

Class Jen(Cain, Sue):

MRO jest od lewej do prawej.

run_the_race
źródło
2

kilka świetnych odpowiedzi tutaj, ale nie dotyczą one sposobu użycia super()w przypadku, gdy różne klasy w hierarchii mają różne podpisy ... szczególnie w przypadku__init__

aby odpowiedzieć na tę część i móc efektywnie korzystać super(), sugerowałbym przeczytanie mojej odpowiedzi super () i zmianę sygnatury metod współpracy .

oto tylko rozwiązanie tego scenariusza:

  1. klasy najwyższego poziomu w Twojej hierarchii muszą dziedziczyć po klasie niestandardowej, takiej jak SuperObject:
  2. jeśli klasy mogą przyjmować różne argumenty, zawsze przekazuj wszystkie otrzymane argumenty do funkcji super jako argumenty słów kluczowych i zawsze akceptuj **kwargs.
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

przykładowe użycie:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

wynik:

E name=python age=28
C age=28
A
D name=python
B
SuperObject
Aviad Rozenhek
źródło
0
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Jest to dość łatwe do zrozumienia.

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

Ok, co się teraz stanie, jeśli użyjesz super(Child,self)?

Gdy tworzona jest instancja potomna, jej MRO (Order Resolution Order) jest w kolejności (Child, SomeBaseClass, obiekt) na podstawie dziedziczenia. (zakładamy, że SomeBaseClass nie ma innych rodziców oprócz domyślnego obiektu)

Przekazując Child, self, superwyszukuje w MRO selfinstancji i zwraca obiekt proxy obok Childa, w tym przypadku jest to SomeBaseClass, obiekt ten następnie wywołuje __init__metodę SomeBaseClass. Innymi słowy, jeśli tak super(SomeBaseClass,self), superzwracany jest obiekt proxyobject

W przypadku wielokrotnego dziedziczenia MRO może zawierać wiele klas, więc w zasadzie superpozwala zdecydować, gdzie chcesz rozpocząć wyszukiwanie w MRO.

dorisxx
źródło
0

Rozważ następujący kod:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

Jeśli zmienisz konstruktora Yna X.__init__, otrzymasz:

X
Y
Q

Ale używając super(Y, self).__init__(), otrzymasz:

X
P
Y
Q

A Pczy Qmoże być nawet zaangażowany z innego pliku, który nie wiem, kiedy piszesz Xi Y. Zasadniczo nie będziesz wiedział, do czego super(Child, self)będzie się odnosić podczas pisania class Y(X), nawet podpis Y jest tak prosty, jak Y(X). Dlatego super może być lepszym wyborem.

tsh
źródło