[gelöst]Vererbung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

[gelöst]Vererbung

Beitragvon jonas » Freitag 9. Januar 2009, 22:22

Nabend.

Ich habe da mal ein Frage zu folgendem Beispiel:

Code: Alles auswählen

class A(object):
   def __init__(self):
      print "<A>"
      super(A, self).__init__()
      print "</A>"

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

class C(A):
   def __init__(self):
      print "<C>"
      super(C, self).__init__()
      print "</c>"

class D(B, C):
   def __init__(self):
      print "<D>"
      super(D, self).__init__()
      print "</D>"

if __name__ == "__main__":
        d = D ()



Wieso gibt das die Ausgabe:

[code=]
<D>
<B>
<C>
<A>
</A>
</c>
</B>
</D>
[/code]

Also warum intialisiert B nicht schon A, weil
A ist ja auch dessen Elternklasse.
Also wenn es geht könnte mir mal jemand
super erklären und sagen ob das noch
genutzt wird und wie.

MfG (der aktuell verwirrte) Jonas
Zuletzt geändert von jonas am Samstag 10. Januar 2009, 19:39, insgesamt 1-mal geändert.
BlackJack

Beitragvon BlackJack » Freitag 9. Januar 2009, 22:38

Lies mal das hier: http://fuhm.net/super-harmful/

Ich benutze Mehrfachvererbung nur für Mixin-Klassen und verwende `super()` grundsätzlich nicht. Wenn man das braucht ist IMHO die Vererbungshierarchie zu kompliziert geworden, und man sollte den Entwurf überdenken.
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Beitragvon jonas » Freitag 9. Januar 2009, 22:56

Danke für die Antwort BlackJack, der Text ist köstlich :D
Kannst du mir mal ein Beispiel nennen wo das
mit dem super sinnvoll ist?
Hier in meinem Beispiel Code kann Klasse C doch auch
die Funktionen aus A und B nutzen:

Code: Alles auswählen

class A (object):

    def __init__ (self):
        print "A"

    def A_s_func (self):
        print "AFunc"

class B (object):

    def __init__ (self):
        print "B"

    def B_s_func (self):
        print "BFunc"

class C (A, B):

    def __init__ (self):
        print "C"

    def C_s_func (self):
        print "CFunc"

if __name__ == "__main__":

    c = C ()
    c.A_s_func()
    c.B_s_func()
    c.C_s_func()


Als Ausgabe habe ich dann halt:
[code=]
C
AFunc
BFunc
CFunc
[/code]

MfG Jonas
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Beitragvon str1442 » Samstag 10. Januar 2009, 01:21

super() wird einem immer / wurde zumindest mir mit Stabilität verkauft, von wegen "man muss die Superklasse nicht direkt in den Quellcode schreiben", allerdings gibt es auch <cls>.__base__ und <cls>.__bases__, zusätzlich muss man bei super auch explitzit den Namen der eigenen Klasse angeben, da self.__class__ zu Endlosrekursion führen kann, womit wiederrum nichts gewonnen ist. Ansonsten wurde super() wohl wegen Problemen bei solchen "Diamant Strukturen", wie du sie da auch (also die Diamant Strukturen, nicht die Probleme) hast, eingeführt, es bringt also eigentlich nur etwas, wenn man Mehrfachvererbung exzessiv nutzt und plötzlich die falschen Methoden ausgewählt werden würden ohne super(), was anscheinend das eigentliche Problem war. Genaueres weiß ich auch nicht.

@dein Anfangsbeispiel: Eigentlich müssten der gesamte Quelltext von oben nach unten abgearbeitet werden? Denke, das sei so definiert?

EDIT: Ich habe gestern Nacht wirklich die __init__()'s übersehen :roll:, und dachte, die prints wären im Namensraum der Klasse geschrieben - da wäre die Ausgabe auch wirklich unlogisch gewesen, so natürlich nicht. Da hätte das Beispiel zwar gar keinen Sinn gemacht, aber nun.
Zuletzt geändert von str1442 am Samstag 10. Januar 2009, 14:04, insgesamt 2-mal geändert.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Samstag 10. Januar 2009, 03:02

My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Samstag 10. Januar 2009, 10:03

@jonas: In Deinem zweiten Beispiel hast Du die `__init__()`\s der Basisklassen gar nicht aufgerufen, das sollte man ja tun.

Und wenn es dann so eine diamantförmige Vererbungsbeziehung gibt wie in Deinem ersten Beispiel, schau Dir mal an was passiert, wenn man das ohne `super()` macht:

Code: Alles auswählen

class A(object):
    def __init__(self):
        print '<A>'
        object.__init__(self)
        print '</A>'


class B(A):
    def __init__(self):
        print '<B>'
        A.__init__(self)
        print '</B>'


class C(A):
    def __init__(self):
        print '<C>'
        A.__init__(self)
        print '</C>'


class D(B, C):
    def __init__(self):
        print '<D>'
        B.__init__(self)
        C.__init__(self)
        print '</D>'


def main():
    d = D()


Dann wird `A.__init__()` dummerweise zweimal aufgerufen:

Code: Alles auswählen

<D>
<B>
<A>
</A>
</B>
<C>
<A>
</A>
</C>
</D>
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Samstag 10. Januar 2009, 11:55

Entspricht "super" nicht einfach dem "next-method" von CLOS bzw. Dylan, wo sich Python doch den Lineralisierungsalgorithmus für die Klassenhierarchie abgeschaut hat? Dieser, so wird doch in einem alten OOPSLA-Paper dargelegt, ist weniger schlecht als alles, was da davor so gab und für graue Haare in Spezialfällen gesorgt hat.

Die Linearisierung vor den "newstyle"-Klassen war ja einfach eine Tiefensuche, in der dann Klassen auch mehrfach vorkommen würden und somit gar keine.

Stefan
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Beitragvon jonas » Samstag 10. Januar 2009, 18:02

Danke für die vielen Antworten :D

@str1442:
Das ist interessant, so ist das also gedacht gewesen mit
der Vererbung.. Danke :D

@BlackJack:
Warum sollte ich die __init__ der Elternklassen aufrufen?
Den Doppelaufruf finde ich eigentlich logisch, weil A ja die sowohl die Elternklasse
von B als auch von C ist.

MfG Jonas 8)
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Samstag 10. Januar 2009, 18:54

jonas hat geschrieben:Warum sollte ich die __init__ der Elternklassen aufrufen?
Den Doppelaufruf finde ich eigentlich logisch, weil A ja die sowohl die Elternklasse
von B als auch von C ist.

Weil du von der Klasse erbst, also alle Verhalten, daher wäre es logisch, wenn du auch das Verhalten des Initializators erbst. Natürlich, es gibt Fälle, in denen das nicht so gedacht ist, aber idR erweitern Kindklassen das verhalten der Elternklassen, sie ersetzen es nicht.

Und Funktionen die man einmal braucht zweimal aufrufen kann dumme Konsequenzen haben. Zum Beispiel wenn in __init__ etwa eine Datenbankverbindung aufgebaut wird, wenn sie zweimal aufgerufen wird, wird eine neue Verbindung erstellt was unnötig ist. Insbesondere wenn man mit mutablen Zuständen Arbeitet (also quasi immer) besteht die Gefahr dass man eine unerwartete Zustandsänderung hat, wenn ``__init__()`` fälschlicherweise mehrfach aufrufen wird.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Beitragvon jonas » Samstag 10. Januar 2009, 19:27

Hi Leonidas!

Sieh mal in meinem Beispiel hier:

Code: Alles auswählen

class A (object):

    def __init__ (self):

        print "Ich bin die Initfunktion von A"

class B (A):

    pass

if __name__ == "__main__":

    b = B ()


Hier bekomme ich die Ausgabe:


[code=]
Ich bin die Initfunktion von A
[/code]

D.h. doch, dass b die Initfunktion
von A erbt ohne das ich super
oder die Initfunktion aufrufe, oder?

MfG Jonas
BlackJack

Beitragvon BlackJack » Samstag 10. Januar 2009, 19:33

Ja, aber nur weil `B` keine eigene `__init__()` hat. Das sah in Deinem letzten Beispiel anders aus. Automatisch wird nur die erste `__init__()` aufgerufen, die gefunden wird. Wenn man eine `__init__()` implementiert, ist man selber dafür verantwortlich die der Basisklasse aufzurufen.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Beitragvon str1442 » Samstag 10. Januar 2009, 19:34

Dann schreib mal def __init__(self): pass in die Definition von B rein.
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Beitragvon jonas » Samstag 10. Januar 2009, 19:38

Ok danke jetzt habe ich es verstanden :D

MfG Jonas

Wer ist online?

Mitglieder in diesem Forum: Google [Bot]