Jak działa funkcja Super () Pythona z dziedziczeniem wielokrotnym?

Jestem całkiem nowy w programowaniu obiektowym Pythona i mam problem zrozumienie funkcji super() (nowe klasy stylu), zwłaszcza jeśli chodzi o dziedziczenie wielokrotne.

Na przykład, jeśli masz coś takiego:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

Nie rozumiem: czy klasa Third() odziedziczy obie metody konstruktora? Jeśli tak, to który z nich będzie uruchamiany super() i dlaczego?

A jeśli chcesz poprowadzić drugą? Wiem, że to ma coś wspólnego z Pythonem. method resolution order ( MRO).
Author: vinzee, 2010-07-18

16 answers

Jest to szczegółowo opisane przez samego Guido w swoim poście na blogu kolejność rozwiązywania metod (w tym dwie wcześniejsze próby).

W twoim przykładzie, Third() wywoła First.__init__. Python szuka każdego atrybutu w rodzicach klasy, ponieważ są one wymienione od lewej do prawej. W tym przypadku szukamy __init__. Więc jeśli zdefiniujesz

class Third(First, Second):
    ...

Python zacznie od spojrzenia na First, a jeśli First nie ma atrybutu, to spojrzy na Second.

Sytuacja ta staje się bardziej złożona, gdy dziedziczenie zaczyna się krzyżować (na przykład Jeśli First odziedziczone od Second). Przeczytaj powyższy link, aby uzyskać więcej informacji, ale w skrócie, Python będzie próbował utrzymać kolejność, w jakiej każda klasa pojawia się na liście dziedziczenia, zaczynając od samej klasy potomnej.

Więc, na przykład, gdybyś miał:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

MRO będzie [Fourth, Second, Third, First].

Przy okazji: jeśli Python nie może znaleźć spójnej kolejności rozwiązywania metod, spowoduje to wyjątek, zamiast powracać do zachowania, które może zaskoczyć użytkownika.

Edytowano, aby dodać przykład wieloznacznego MRO:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Powinien Third być [First, Second] czy [Second, First]? Nie ma oczywistych oczekiwań, a Python wywoła błąd: {]}

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

Edit: widzę, że kilka osób argumentuje, że w powyższych przykładach brak super() wywołań, więc pozwól mi wyjaśnić: celem przykładów jest pokazanie, jak jest skonstruowany MRO. Są nie przeznaczone do wydruku "first\nsecond \ third" lub cokolwiek innego. Możesz-i powinieneś, oczywiście, pobawić się tym przykładem, dodać wywołania super(), zobaczyć, co się stanie i uzyskać głębsze zrozumienie modelu dziedziczenia Pythona. Ale moim celem tutaj jest, aby zachować to proste i pokazać, jak jest zbudowany MRO. I jest zbudowany jak wyjaśniłem:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)
 759
Author: rbp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-26 12:36:24

Twój kod i inne odpowiedzi są wadliwe. Brakuje im wywołań super() w dwóch pierwszych klasach, które są wymagane do pracy z podklasą kooperacyjną.

Oto stała wersja kodu:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

Wywołanie super() znajduje następną metodę w MRO na każdym kroku, dlatego też pierwsza i druga muszą ją mieć, w przeciwnym razie wykonanie zatrzyma się na końcu Second.__init__().

Oto co dostaję:

>>> Third()
second
first
third
 274
Author: lifeless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-09 01:33:44

Chciałem nieco rozwinąć odpowiedź lifeless, ponieważ kiedy zacząłem czytać o tym, jak używać super() w hierarchii dziedziczenia Wielokrotnego w Pythonie, nie dostałem jej od razu.

Musisz zrozumieć, że super(MyClass, self).__init__() zapewnia następny __init__ metoda zgodnie z zastosowanym algorytmem Resolution Ordering (MRO) w kontekście kompletnej hierarchii dziedziczenia .

Ta ostatnia część jest kluczowa do zrozumienia. Let ' s rozważmy przykład jeszcze raz:
#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

Według tego artykułu o metodzie Resolution Order autorstwa Guido van Rossuma, kolejność rozwiązywania __init__ jest obliczana (przed Pythonem 2.3) przy użyciu "głębokości-pierwszego przejścia od lewej do prawej": {31]}

Third --> First --> object --> Second --> object

Po usunięciu wszystkich duplikatów, z wyjątkiem ostatniego, otrzymujemy:

Third --> First --> Second --> object

Przyjrzyjmy się więc, co się dzieje, gdy tworzymy instancję klasy Third, np. x = Third().

  1. według MRO Third.__init__ wykonuje.
    • druki Third(): entering
    • następnie super(Third, self).__init__() wykonuje i MRO zwraca First.__init__ które jest wywołane.
  2. First.__init__ wykonuje.
    • druki First(): entering
    • następnie super(First, self).__init__() wykonuje i MRO zwraca Second.__init__ które jest wywołane.
  3. Second.__init__ wykonuje.
    • druki Second(): entering
    • następnie super(Second, self).__init__() wykonuje i MRO zwraca object.__init__ które jest wywołane.
  4. object.__init__ wykonuje (brak poleceń print w kodzie tam)
  5. wykonanie wraca do Second.__init__, które następnie drukuje Second(): exiting
  6. wykonanie wraca do First.__init__, które następnie drukuje First(): exiting
  7. wykonanie wraca do Third.__init__, które następnie drukuje Third(): exiting

To wyjaśnia, dlaczego utworzenie instancji Third() powoduje:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

Algorytm MRO został ulepszony od Pythona 2.3, aby działał dobrze w złożonych przypadkach, ale myślę, że używając "głębokości-pierwszy lewo-prawo trawersalu" + " usuwanie duplikatów oczekiwać dla ostatni" nadal działa w większości przypadków(proszę skomentować, jeśli tak nie jest). Koniecznie przeczytaj post na blogu Guido!

 199
Author: Visionscaper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-27 17:39:36

Jest to znane jako problem Diamond , strona ma wpis dotyczący Pythona, ale krótko mówiąc, Python wywoła metody superklasy od lewej do prawej.

 59
Author: monoceres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-02 09:52:10

Oto jak rozwiązałem problem posiadania wielu dziedziczeń z różnymi zmiennymi do inicjalizacji i posiadania wielu Mixinów z tym samym wywołaniem funkcji. Musiałem jawnie dodać zmienne do przekazywanych * * kwargs i dodać interfejs MixIn jako punkt końcowy dla super połączeń.

Tutaj A jest rozszerzalną klasą bazową, a B i C są klasami MixIn, które dostarczają funkcję f. A i B oba oczekują parametru v w swoich __init__ i C oczekuje w. Funkcja f przyjmuje jeden parametr y. Q dziedziczy wszystkie trzy klasy. MixInF jest interfejsem mixin dla B i C.


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)
 32
Author: brent.payne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-17 19:22:31

Rozumiem, że to nie odpowiada bezpośrednio na pytanie super(), ale uważam, że jest wystarczająco istotne, aby się podzielić.

Istnieje również sposób bezpośredniego wywołania każdej odziedziczonej klasy:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

Zwróć uwagę, że jeśli zrobisz to w ten sposób, będziesz musiał wywołać każde ręcznie, ponieważ jestem prawie pewien, że First ' S __init__() nie zostaną wywołane.

 30
Author: Seaux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-09 01:31:00

Ogólnie

Zakładając, że wszystko pochodzi z object (jesteś zdany na siebie, jeśli nie), Python oblicza kolejność rozwiązywania metod (MRO) na podstawie drzewa dziedziczenia klas. MRO spełnia 3 właściwości:

  • dzieci z klasy przychodzą przed rodziców
  • Lewi rodzice są przed prawymi rodzicami
  • Klasa pojawia się tylko raz w MRO

Jeśli takie porządkowanie nie istnieje, błędy Pythona. Wewnętrzne działanie tego jest C3 Lineryzacja klas. Przeczytaj o tym tutaj: https://www.python.org/download/releases/2.3/mro/

Tak więc w obu poniższych przykładach jest to:

  1. dziecko
  2. Left
  3. prawo
  4. rodzic

Gdy metoda jest wywoływana, pierwszym wystąpieniem tej metody w MRO jest ta, która jest wywoływana. Każda klasa, która nie implementuje tej metody jest pomijana. Każde wywołanie super wewnątrz tej metody wywoła następne występowanie tej metody w por. W związku z tym liczy się zarówno kolejność umieszczania klas w dziedziczeniu, jak i miejsce, w którym umieszczasz wywołania do super w metodach.

Z super najpierw w każdej metodzie

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"

Child() Wyjścia:

parent
right
left
child

Z super Ostatni w każdej metodzie

class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()

Child() Wyjścia:

child
left
right
parent
 24
Author: Zags,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 20:35:43

About @ calf ' s comment , możesz użyć, jak zwykle, **kwargs:

Przykład biegania online

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

Wynik:

A None
B hello
A1 6
B1 5

Możesz również użyć ich pozycyjnie:

B1(5, 6, b="hello", a=None)
[7]}ale musisz pamiętać MRO, to naprawdę mylące. Można tego uniknąć, używając parametrów tylko dla słowa kluczowego :
class A(object):
  def __init__(self, *args, a, **kwargs):
    print("A", a)

Itd.

Mogę być trochę irytujący, ale zauważyłem, że ludzie zapomnieli za każdym razem użyć *args i **kwargs kiedy nadpisują metodę, chociaż jest to jeden z niewielu naprawdę przydatne i rozsądne wykorzystanie tych "magicznych zmiennych".

 19
Author: Marco Sulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-16 19:22:56

Innym jeszcze nie zakrytym punktem jest przekazywanie parametrów do inicjalizacji klas. Ponieważ cel super zależy od podklasy, jedynym dobrym sposobem na przekazanie parametrów jest spakowanie ich wszystkich razem. Następnie należy uważać, aby nie mieć tej samej nazwy parametru o różnych znaczeniach.

Przykład:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

Daje:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

Wywołanie super klasy __init__ bezpośrednio do bardziej bezpośredniego przypisania parametrów jest kuszące, ale nie powiedzie się, jeśli istnieje jakieś super wywołanie w super klasie i / lub MRO jest zmieniany i klasa A może być wywoływana wielokrotnie, w zależności od implementacji.

Podsumowując: dziedziczenie kooperacyjne i super i specyficzne parametry inicjalizacji nie działają ze sobą zbyt dobrze.

 15
Author: Trilarion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-29 11:57:13
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

Wyjście To

first 10
second 20
that's it

Wywołanie Third() lokalizuje init zdefiniowany w Third. I call to super w tej rutynie wywołuje init zdefiniowany w First. MRO=[pierwszy, drugi]. Teraz wywołanie do super in INIT zdefiniowane w pierwszym będzie kontynuować wyszukiwanie MRO i znaleźć INIT zdefiniowane w drugim, a każde wywołanie do super trafi w domyślny obiekt init. Mam nadzieję, że ten przykład wyjaśnia tę koncepcję.

Jeśli nie zadzwonisz najpierw do super. Zatrzymuje się łańcuch i otrzymasz następujące wyjście.
first 10
that's it
 5
Author: Seraj Ahmad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-21 02:23:34

W learningpythonthehardway uczę się czegoś o nazwie super() wbudowanej funkcji, jeśli się nie mylę. Wywołanie funkcji super() może pomóc dziedziczeniu przejść przez rodzica i "rodzeństwo" i pomóc ci lepiej widzieć. Nadal jestem początkujący, ale uwielbiam dzielić się swoimi doświadczeniami na temat korzystania z tego super () w python2. 7.

Jeśli przeczytałeś komentarze na tej stronie, usłyszysz o method Resolution Order (MRO), metoda jest funkcją, którą napisałeś, MRO będzie używać Depth-First-Left-To-Right scheme to search and run. Możesz zrobić więcej badań na ten temat.

Przez dodanie funkcji super ()

super(First, self).__init__() #example for class First.

Możesz połączyć wiele instancji i 'rodzin' Za pomocą super(), dodając w każdej i wszystkich w nich. I będzie wykonywać metody, przejść przez nich i upewnić się, że nie przegapisz! Jednak dodanie ich przed lub po nie robi różnicy, będziesz wiedział, czy wykonałeś ćwiczenie learningpythonthehardway 44. Niech zabawa zaczyna się!!

Biorąc przykład poniżej, możesz skopiować i wkleić i spróbować go uruchomić:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()
Jak to działa? Przykład fifth () will wygląda tak. Każdy krok przechodzi od klasy do klasy, w której dodano funkcję super.
1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

Rodzic został znaleziony i będzie kontynuowany do trzeciego i czwartego!!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

Teraz wszystkie klasy z super() zostały udostępnione! Klasa rodzica została znaleziona i wykonana, a teraz nadal rozpakowuje funkcję w dziedziczeniach do skończyłem kody.

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

Wynik powyższego programu:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

Dla mnie dodanie super() pozwala mi zobaczyć jaśniej, jak python wykonałby moje kodowanie i upewnić się, że dziedziczenie może uzyskać dostęp do metody, którą zamierzałem.

 4
Author: Disrudog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-25 11:41:04

Rozważ wywołanie super().Foo() wywołane z podklasy. Metoda method Resolution Order (MRO) jest to kolejność, w jakiej wywołania metody są rozwiązywane.

Przypadek 1: Pojedyncze Dziedziczenie

W tym, super().Foo () będzie wyszukiwane w hierarchii i rozważy najbliższą implementację, jeśli zostanie znaleziona, w przeciwnym razie wywoła wyjątek. Na "jest" relacja zawsze będzie prawdziwa pomiędzy dowolną odwiedzaną podklasą a jej superklasą w hierarchii. Ale ta historia nie jest zawsze taka sama w wielokrotnym dziedziczeniu.

Przypadek 2: Dziedziczenie Wielokrotne

Tutaj, podczas wyszukiwania super ().Implementacja Foo (), każda odwiedzana klasa w hierarchii może lub nie musi mieć jest relacją . Rozważmy następujące przykłady:

class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass

Tutaj, {[2] } jest najniższą klasą w hierarchii. Diagram hierarchii i MRO dla {[2] } będzie

Tutaj wpisz opis obrazka

(czerwone cyfry pokazujące MRO)

MRO jest I E C D A H F G B object

Zauważ, że klasa X będzie odwiedzana tylko wtedy, gdy wszystkie jej podklasy, które z niej dziedziczą, zostały odwiedzone(tzn. nigdy nie powinieneś odwiedzać klasy, która ma strzałkę wchodzącą do niej z klasy poniżej, której jeszcze nie odwiedziłeś).

Tutaj zauważ, że po wizycie na zajęciach C , D jest odwiedzana, chociaż C i D nie mają jest relacją między nimi(ale obie mają z A). Tutaj super() różni się od singla spadek.

Rozważmy nieco bardziej skomplikowany przykład:]}

Tutaj wpisz opis obrazka

(czerwone cyfry pokazujące MRO)

MRO to I E C H D A F G B object

W tym przypadku przechodzimy od I do E do C. Następnym krokiem będzie A, ale musimy jeszcze odwiedzić D, podklasę A. Nie możemy jednak odwiedzić D, ponieważ musimy jeszcze odwiedzić H, podklasę D. Liście H jako następna klasa do odwiedzenia. Pamiętaj, staramy się wejść w hierarchii, jeśli to możliwe, więc odwiedzamy jej najbardziej po lewej stronie nadklasy, D. Po D odwiedzamy A, ale nie możemy przejść do obiektu, ponieważ jeszcze nie odwiedziliśmy F, G, i B. Klasy te, w kolejności, uzupełniają MRO dla I.

Zauważ, że żadna klasa nie może pojawić się więcej niż raz w MRO.

Tak wygląda super() w hierarchii dziedziczenia.

Kredyty dla zasobów: Richard L Halterman Podstawy programowania w Pythonie

 4
Author: mradul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-06 09:54:13

Chciałbym dodać do to, co mówi @Visionscaper na górze:

Third --> First --> object --> Second --> object

W tym przypadku interpreter nie odfiltrowuje klasy obiektu z powodu jej duplikacji, a raczej dlatego, że Second pojawia się w pozycji head i nie pojawia się w pozycji tail w podgrupie hierarchii. Podczas gdy obiekt pojawia się tylko w pozycji ogonowej i nie jest uważany za silną pozycję w algorytmie C3 do określenia priorytetu.

Linearyzacja (mro) klasy C, L (C), jest

  • Klasa C
  • plus połączenie z
    • linearyzacja jego rodziców P1, P2, .. = L (P1, P2, ...) i
    • lista jego rodziców P1, P2, ..

Linearyzowane Scalanie odbywa się poprzez wybranie klas wspólnych, które pojawiają się jako głowa list, a nie ogon, ponieważ kolejność ma znaczenie(stanie się jasna poniżej)

Liniowość trzeciej można obliczyć w następujący sposób:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

Tak więc dla implementacji super() w następujący kod:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

Staje się oczywiste, jak ta metoda zostanie rozwiązana

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"
 3
Author: supi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-10 11:16:15

W Pythonie 3.5 + dziedziczenie wygląda dla mnie przewidywalnie i bardzo ładnie. Proszę spojrzeć na ten kod:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

Wyjścia:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

Jak widzisz, wywołuje foo dokładnie jeden raz dla każdego odziedziczonego łańcucha w tej samej kolejności, w jakiej został odziedziczony. Zamówienie można uzyskać dzwoniąc .Mro :

Czwarty -> trzeci -> Pierwszy -> drugi -> baza - > obiekt

 3
Author: rfedorov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-10 16:17:53

Może jest jeszcze coś, co można dodać, mały przykład z Django rest_framework i dekoratorami. Daje to odpowiedź na Ukryte pytanie: "po co miałbym tego chcieć?"

Jak powiedziałem: jesteśmy z Django rest_framework i używamy ogólnych widoków, a dla każdego typu obiektów w naszej bazie danych znajdujemy się z jedną klasą view dostarczającą GET I POST dla list obiektów, a inną klasą view dostarczającą GET, PUT I DELETE dla poszczególnych obiektów.

Teraz POST, PUT i DELETE chcemy udekorować Login_required Django. Zauważ, jak to dotyka obu klas, ale nie wszystkich metod w żadnej z nich.

Rozwiązanie może przejść przez wielokrotne dziedziczenie.

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

Podobnie dla innych metod.

Do listy moich konkretnych klas dodałbym Moje LoginToPost Przed ListCreateAPIView i LoginToPutOrDelete przed RetrieveUpdateDestroyAPIView. Moje konkretne zajęcia " get pozostaną bez dekoracji.

 2
Author: mariotomo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-07 20:48:45

Zamieszczanie tej odpowiedzi na moje przyszłe referencje.

Dziedziczenie wielokrotne Pythona powinno używać modelu diamond, a podpis funkcji nie powinien się zmieniać w modelu.

    A
   / \
  B   C
   \ /
    D

Przykładowy fragment kodu to ;-

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

Tutaj Klasa A to object

 1
Author: Akhil Nadh PC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-10 07:08:31