Seite 1 von 1
[gelöst]Vererbung
Verfasst: Freitag 9. Januar 2009, 22:22
von jonas
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:
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
Verfasst: Freitag 9. Januar 2009, 22:38
von 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.
Verfasst: Freitag 9. Januar 2009, 22:56
von jonas
Danke für die Antwort BlackJack, der Text ist köstlich
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:
MfG Jonas
Verfasst: Samstag 10. Januar 2009, 01:21
von str1442
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

, 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.
Verfasst: Samstag 10. Januar 2009, 03:02
von Leonidas
Verfasst: Samstag 10. Januar 2009, 10:03
von 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:
Verfasst: Samstag 10. Januar 2009, 11:55
von sma
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
Verfasst: Samstag 10. Januar 2009, 18:02
von jonas
Danke für die vielen Antworten
@str1442:
Das ist interessant, so ist das also gedacht gewesen mit
der Vererbung.. Danke
@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

Verfasst: Samstag 10. Januar 2009, 18:54
von Leonidas
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.
Verfasst: Samstag 10. Januar 2009, 19:27
von jonas
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:
D.h. doch, dass b die Initfunktion
von A erbt ohne das ich super
oder die Initfunktion aufrufe, oder?
MfG Jonas
Verfasst: Samstag 10. Januar 2009, 19:33
von 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.
Verfasst: Samstag 10. Januar 2009, 19:34
von str1442
Dann schreib mal def __init__(self): pass in die Definition von B rein.
Verfasst: Samstag 10. Januar 2009, 19:38
von jonas
Ok danke jetzt habe ich es verstanden
MfG Jonas