[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.
Antworten
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

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: Alles auswählen

<D>
<B>
<C>
<A>
</A>
</c>
</B>
</D>
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

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

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: Alles auswählen

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

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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

@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

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

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)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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 (former) Modvoice
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

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: Alles auswählen

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

MfG Jonas
BlackJack

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

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

Ok danke jetzt habe ich es verstanden :D

MfG Jonas
Antworten