Vererbung: Superconstructor aufrufen ... (Level 2, 3, 4 ...)

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
moe.pub
User
Beiträge: 4
Registriert: Samstag 28. Mai 2011, 17:41

Guten Tag meine Damen und Herren,

ich stehe gerade vor einem kleinen Problem mit meiner Vererbungshierarchie und finde einfach keine ellegante Loesung.
Als beispiel habe ich mal folgende Klassenhierarchie erstellt, die das Problem verdeutlichen soll:

Code: Alles auswählen

class A(object):
	def __init__(self, param):
		print 'A'

class B(A):
	def __init__(self, param1, param2):
#		super(self.__class__, self).__init__(param1)
#		<super: <class 'B'>, <B object>>
#		print super(self.__class__, self)
		print 'B'
		

class C(B):
	def __init__(self, param1, param2):
#		super(self.__class__, self).__init__(param1, param2)
#		<super: <class 'C'>, <C object>
#		print super(self.__class__, self)
		print 'C()'

class D(C):
	def __init__(self, param1, param2):
#		super(self.__class__, self).__init__(param1, param2)
		print 'D()'
Jetzt moechte ich in jedem Constructor natuerlich den Constructor der Basisklasse aufrufen, will heissen, wenn ich eine Instanz von D anlege, dann soll folgende Ausgabe produziert werden:

A
B
C
D

Nun habe ich die Funktion super() gefunden, die wie es mir scheint, allerdings fuer diese Aufgabe nicht wirklich geeignet ist.
Wenn ich in jedem der Constructoren "super(self.__class__, self).__init__(param1, param2)" einfuege, so gibt es ab der Klasse C Probleme:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 3, in __init__
TypeError: __init__() takes exactly 3 arguments (2 given),

und bei Klasse D geht gar nicht's mehr. Nun habe ich nachgelesen und mir scheint es, dass super nur eine bestimmte Klasse aus der Vererbungshierarchy herausnimmt, und dann den Methodenaufruf an die entsprechende Superklasse weiterleitet.
In der Theorie klingt das ganz gut, nur scheint das irgendwie nicht zu funktionieren.

Ich hoffe mir kann hier weitergeholfen werden, denn ich moechte nicht zurueck zu der Methode, wo die Superklasse im Constructor explizit aufscheint.

Daher, bitte um Hilfe ...

Gruesse,
MoE
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Dein Fehler liegt beim Aufbau der Klassen.
Ich versteh zwar nicht was du machen willst aber schau dir mal das Klassensystem von Python an

Code: Alles auswählen

class A(object):
        def __init__(self):
                print 'A'

class B(object):
        def __init__(self):
                print 'B'
                A()

class C(object):
        def __init__(self):
                print 'C'
                B()

class D(object):
        def __init__(self):
                print 'D'
                C()
D()
MfG Tompazi
moe.pub
User
Beiträge: 4
Registriert: Samstag 28. Mai 2011, 17:41

Aber so sind das nur einzelne Klassen, die nicht voneinander ableiten.
Jede Klasse soll von der anderen ableiten und jeweils im Konstruktor den der Superklasse aufrufen (ohne dass der Name der Superklasse explizit zum Aufruf des Superkonstruktors verwendet wird). Es soll eine Vererbungshierarchie entstehen.

Die Vererbung wird benötigt, um generisch Zugriff auf sämtliche Operatoren zu haben.
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Code: Alles auswählen

class A(object):
	def __init__(self, param):
		print 'A'

class B(A):
	def __init__(self, param1, param2):
		A.__init__(self, param1, param2)
		print 'B'
		

class C(B):
	def __init__(self, param1, param2):
                B.__init__(self, param1, param2)
		print 'C()'

class D(C):
	def __init__(self, param1, param2):
		C.__init__(self, param1, param2)
		print 'D()'
Sollte das gewünschte Ergebnis zeitigen. Den Namen der direkten Vaterklasse kennst und brauchst du auf jeden Fall, also kannst du ihn doch auch in der init verwenden, oder?

Edit: Parameter vergessen.
moe.pub
User
Beiträge: 4
Registriert: Samstag 28. Mai 2011, 17:41

Aus Sicht eines Softwareentwickler ist das aber nicht schön. Das muss doch irgendwie sonst gehn ...

Dein Vorschlag ist mir natürlich bekannt, halte das ganze aber für nicht sauber.
BlackJack

@moe.pub: So macht man das aber.

Warum `super()` bei Dir so nicht funktioniert kannst Du hier nachlesen: http://fuhm.net/super-harmful/

Ich find's ziemlich unbenutzbar und verwirrend. Und da ich keine Mehrfachvererbung verwende, tue ich mir das nicht an.
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Ich bin Software-Entwickler. Was ist daran nicht schön? Daß du ihm sagen mußt, wo er die Methode zu suchen hat? Es gibt Fälle, in denen der Konstruktor (ist __init__ ja eigentlich nicht, aber das lassen wir mal außen vor) der Vaterklasse gar nicht aufgerufen werden soll. Oder halt erst nachdem der "neue" Code durch ist. Insofern kann man den Konstruktoraufruf der Vaterklasse nicht automatisieren. Und ob du nun 'super.__init__' oder 'A.__init__' schreibst ist doch eigentlich gehüpft wie gesprungen. An der Stelle 'classX(A)' brauchst du die Information doch ohnehin.
moe.pub
User
Beiträge: 4
Registriert: Samstag 28. Mai 2011, 17:41

@BlackJack: Mehrfachvererbung ist was anderes

@Pekh
Ich kenn jetzt keine andere Sprache, wo eine Methode der Superklasse so kompliziert aufgerufen werden muss.
(OCamel hat ich noch nie die Objektorientierten Features benutzt, aber der Sprache traue ich das zu)

Warum A.__init__(self, param1, param2) mir nicht zusagt hat mehrere Gruende. Es erinnert mich sehr viel mehr an einen Aufruf einer statischen Methode. Ich denke, wenn ich den Code in einem Jahr wieder durchlese, ... Und ausserdem, wenn ich so an refactoring denke, naja, wie gesagt, es gibt mehrere Gruende warum mir das Konzept nicht gefaellt.

Und das der Superkonstruktor nicht oder nur nach einigen Initialisierungsmassnahmen aufgerufen werden soll ....
Ich wuerde so etwas in meinem ganzen Leben nicht machen. Da wuerde ich eher die Klassenhierarchie umstellen oder gewisse Sachen deligieren.

Aber ich werde mich der Sprache wohl fuegen muessen, das Projekt ist mittlerweile schon so fortgeschritten dass ein Umstieg auf eine andere Sprache zu viel Zeit verursachen wuerde.
BlackJack

@moe.pub: Mehrfachvererbung ist etwas anderes als *was*? Dass Du da keine hast ist mir klar — genau deswegen kannst Du ja auf `super()` verzichten. Du würdest es ja aber anscheinend trotzdem gerne verwenden. Dann musst Du aber auch mit den Eigenarten davon leben. Und verstehen warum es in Deinem Beispiel nicht funktioniert.

Was ist an dem Aufruf so kompliziert? Du rufst die Funktion auf der Elternklasse doch ziemlich einfach und direkt auf. Die Elternklasse musst Du angeben, weil es davon mehrere geben kann und es sonst nicht eindeutig wäre. Dann folgt der Funktionsname und die Argumente. Du rufst halt die Funktion auf und keine Methode, denn auf der Klasse sind die Attribute noch keine gebundenen Methoden. ``A.f(x, 42)`` ist ja das selbe wie ``x.f(42)`` wenn `x` vom Typ `A` ist. Den Typ muss man wie gesagt angeben, und ansonsten ist es nicht komplizierter.
Antworten