Lässt sich eine vererbte Methode erweitern?

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.
mutetella
User
Beiträge: 1690
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Dienstag 19. Juli 2011, 19:15

Hallo,

wenn Klasse B von Klasse A abgeleitet ist, gibt es dann einen Weg, eine Methode aus Klasse A nicht zu überschreiben sondern zu erweitern.
Anderst gesagt: Wenn die Methode ausgeführt wurde noch weitere Anweisungen bzw. eine weitere Methode auszuführen?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dienstag 19. Juli 2011, 19:29

Nicht im Python-Objekt-Modell. Du kannst aber die Methode ueberschreiben und in der Ueberschreibenden explizit die Ueberschriebene aufrufen.
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Dienstag 19. Juli 2011, 19:37

Diesen Fall solltest du am besten in der Elternklasse behandeln:

Code: Alles auswählen

>>> class Spam(object):
...     def foo(self):
...         print "foo"
...         self.bar()
...         print "baz"
...     def bar(self):
...         pass
... 
>>> class Eggs(Spam):
...     def bar(self):
...         print "bar"
... 
>>> spam = Spam()
>>> eggs = Eggs()
>>> spam.foo()
foo
baz
>>> eggs.foo()
foo
bar
baz
`bar` ist hier die optionale Erweiterung.
Das Leben ist wie ein Tennisball.
DasIch
User
Beiträge: 2473
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Dienstag 19. Juli 2011, 19:40

Du könntest die überschriebene Methode, in einer Metaklasse, mit einer Methode ersetzten die tut was du willst.
mutetella
User
Beiträge: 1690
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Dienstag 19. Juli 2011, 20:00

@EyDu:
Das geht allerdings nur dann, wenn ich die Elternklasse auch verändern kann. Wenn ich z. B. von 'list' oder 'dict' erbe und die Methodennamen in der Kindklasse unverändert bleiben sollen, kann ich das so nicht machen...

@DasIch:
Um die Methode zu ersetzen müsste ich allerdings wissen, was die Elternmethode genau macht.

@cofi:
Genau so stelle ich mir das vor. Ich hatte nur bis gerade eben keine Vorstellung, wie ich das Original der überschriebenen Methode aufrufen kann.
Bin gerade auf dieses Beispiel zur Verwendung von 'super()' gestoßen.
Damit kann ich dann so Zeugs machen wie:

Code: Alles auswählen

class List_(list):
    def __init__(self, iterable):
        list.__init__(iterable)

    def append(self, item):
        print 'Make some stuff before...'
        super(List_, self).append(item)
        print '... and make others after it!'
Ich verstehe allerdings noch nicht, weshalb es nicht

Code: Alles auswählen

super(list, self).append(item)
heißen muss...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dienstag 19. Juli 2011, 20:10

mutetella hat geschrieben:Ich verstehe allerdings noch nicht, weshalb es nicht

Code: Alles auswählen

super(list, self).append(item)
heißen muss...
Ok, schaun wir uns doch mal an, was das bedeuten koennte.
Du gibst die Elternklasse an, deren Methode du haben moechtest dazu die Instanz und rufst auf dem Rueckgabewert eine Methode auf.
Welchen Unterschied sollte das zu `list.append(self, item)` haben? Und wie sollte sich aus einer Elternklasse die richtige Elternklasse bestimmen lassen?

Nein, man benoetigt die Klasse, von der man eine Elternklasse benoetigt.
Die Syntax ist allerdings reichlich gewoehnungsbeduerftig und man hat sich in Python 3 gluecklicherweise darum gekuemmert. Vergleiche http://docs.python.org/py3k/library/fun ... html#super mit http://docs.python.org/library/functions.html#super
mutetella
User
Beiträge: 1690
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Mittwoch 20. Juli 2011, 11:23

@cofi:
Sorry, ich hab's echt versucht, aber ich verstehe Deine Erklärung nicht... ;-)
cofi hat geschrieben:Du gibst die Elternklasse an, deren Methode du haben moechtest dazu die Instanz und rufst auf dem Rueckgabewert eine Methode auf.
Eben. Weshalb also muss ich 'super()' als erstes Argument die Kindklasse und als zweites Argument dann die Instanz, die ja aus der Kindklasse hervorgeht, übergeben?
Warum als erstes Argument nicht die Elternklasse, deren Methode ich ja in Verbindung mit der Instanz (zweites Argument) aufrufen (Rückgabewert) möchte?
cofi hat geschrieben:Welchen Unterschied sollte das zu `list.append(self, item)` haben?
Na eben den, dass in der Python 2-Variante der Aufruf nicht 'list.append(self, item)' sondern 'List_.append(self, item)' bedeutet. Und genau das finde ich merkwürdig.
cofi hat geschrieben:Und wie sollte sich aus einer Elternklasse die richtige Elternklasse bestimmen lassen?
Indem ich die eine, wahre Elternklasse angebe? Und nicht auf eines der Kinder zurückgreife?
cofi hat geschrieben:Die Syntax ist allerdings reichlich gewoehnungsbeduerftig ...
Meinst Du dasselbe wie ich?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
deets

Mittwoch 20. Juli 2011, 11:34

mutetella hat geschrieben:@cofi:
Weshalb also muss ich 'super()' als erstes Argument die Kindklasse und als zweites Argument dann die Instanz, die ja aus der Kindklasse hervorgeht, übergeben?
Warum als erstes Argument nicht die Elternklasse, deren Methode ich ja in Verbindung mit der Instanz (zweites Argument) aufrufen (Rückgabewert) möchte?
Ganz einfach: nehmen wir mal an, du hast Klassen A & C, C leitet von A ab. Jetzt waere Python so, wie du es beschreibst - man wuerde in C.foo A.foo aufrufen, indem man sagt

super(A, self).foo()

Und jetzt kommst du auf die Idee, ein B einzufuerhen, das foo ueberlaedt, und davon leitet jetzt dein C zusaetzlich ab. Und jetzt steht da in deinem Code, man soll aber bitteschoen von A das foo aufrufen.

Das ist der Grund. super liefert die naechste Klasse in der sogenannten Method-Resolution-Order der *aktuellen* Klasse. Nicht von irgendeiner Klasse weiter oben in der Typhierarchie.
BlackJack

Mittwoch 20. Juli 2011, 11:40

@mutetella: Die *eine, wahre* Elternklasse gibt es nicht. Jedenfalls nicht wenn man nicht nur eine Elternklasse hat.

Du könntest von `super()` auch einfach die Finger lassen. Lies Dir mal durch wie `super()` funktioniert und welche Einschränkungen das mit sich bringt: http://fuhm.net/super-harmful/
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Mittwoch 20. Juli 2011, 12:50

mutetella hat geschrieben:
cofi hat geschrieben:Du gibst die Elternklasse an, deren Methode du haben moechtest dazu die Instanz und rufst auf dem Rueckgabewert eine Methode auf.
Eben. Weshalb also muss ich 'super()' als erstes Argument die Kindklasse und als zweites Argument dann die Instanz, die ja aus der Kindklasse hervorgeht, übergeben?
Warum als erstes Argument nicht die Elternklasse, deren Methode ich ja in Verbindung mit der Instanz (zweites Argument) aufrufen (Rückgabewert) möchte?
Wenn du das tust, welchen Sinn hat dann der `super`-Aufruf? `super` ist dazu da, die _richtige_ Elternklasse auszuwählen.
Nehmen wir mal an wir haben ein `class A (B, C, D): ..` gebe ich jetzt dem `super` Aufruf nur ein `B` an, schneide ich 2 Drittel der nötigen Informationen ab.
`super` tut einfach etwas anderes als du denkst.
Die Instanz der Kindklasse ist nur nötig, damit am Ende ein Instanzobjekt rausfällt.
cofi hat geschrieben:Welchen Unterschied sollte das zu `list.append(self, item)` haben?
Na eben den, dass in der Python 2-Variante der Aufruf nicht 'list.append(self, item)' sondern 'List_.append(self, item)' bedeutet. Und genau das finde ich merkwürdig.[/quote]
Nein eben nicht, würde da ein `List_.append` rausfallen hätten wir eine Endlosrekursion.
mutetella hat geschrieben:
cofi hat geschrieben:Und wie sollte sich aus einer Elternklasse die richtige Elternklasse bestimmen lassen?
Indem ich die eine, wahre Elternklasse angebe? Und nicht auf eines der Kinder zurückgreife?
Neben der Tatsache, dass es nicht nur eine gibt: Wenn ich die schon angebe warum sollte ich sie noch bestimmen?
cofi hat geschrieben:Die Syntax ist allerdings reichlich gewoehnungsbeduerftig ...
Meinst Du dasselbe wie ich?[/quote]
Nein ich denke nicht, für mich ist sie nicht "falsch" sondern eher umständlich.
mutetella
User
Beiträge: 1690
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Mittwoch 20. Juli 2011, 13:42

Ok, ich beginne langsam zu verstehen, vielen Dank für Eure Mühe!!

Ich frage mich allerdings, weshalb es 'super()' überhaupt gibt, wenn doch ein 'SuperClass.method(self, arg)' direkt zum Ziel führt?
Folgende Aussage aus BlackJack's Link ist mir dabei nicht ganz klar:
"The only situation in which super() can actually be helpful is when you have diamond inheritance."
siehe hier
Was ist hier unter 'rautenförmiger (??) Vererbung' zu verstehen?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Mittwoch 20. Juli 2011, 13:49

`super` gibt es, weil Mehrfachvererbung die Sache etwas komplizierter macht.

Code: Alles auswählen

class A():
    pass
class B(A):
    pass
class C(A):
    pass
class D(B, C):
   pass
Zeichne dir mal ein Diagramm, wer von dem erbt, wenns dir auf einmal bayrisch wird hast du keinen Fehler gemacht ;)
deets

Mittwoch 20. Juli 2011, 13:51

mutetella hat geschrieben:Ok, ich beginne langsam zu verstehen, vielen Dank für Eure Mühe!!

Ich frage mich allerdings, weshalb es 'super()' überhaupt gibt, wenn doch ein 'SuperClass.method(self, arg)' direkt zum Ziel führt?
Noe, du begreifst es offensichtlich immer noch nicht. Vielleicht hilft ja etwas Code, wo die Worte nicht ausreichen:

Code: Alles auswählen



class A(object):

    def __init__(self):
        super(A, self).__init__()
        print "A.__init__"


class B(A):

    def __init__(self):
        super(B, self).__init__()
        print "B.__init__"


class C(A):

    def __init__(self):
        super(C, self).__init__()
        print "C.__init__"


class D(B, C):

    def __init__(self):
        # hier mal gaaaaaanz genau hinschauen
        # und die Anzahl der Aufrufe im Code
        # versus die Anzahl der ausgegebenen vergleichen
        super(D, self).__init__()
        print "D.__init__"

D()

Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

Mittwoch 20. Juli 2011, 15:53

Also ich glaub das ist ungefähr das gleiche,was ihr die ganze zeit besprecht, aber geht das nicht auch so:

Code: Alles auswählen

class Mutter():
    def hello(self,text):
        print(text)

class Tochter(Mutter):
    def __init__(self):
        self.hello_ur=Mutter.hello
        

    def hello(self,text):
        self.hello_ur("dummy","none")
        print(text)

        
        
        
a=Tochter()
a.hello("uhii")
So hab ich das immer gemacht
Benutzeravatar
/me
User
Beiträge: 3206
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Mittwoch 20. Juli 2011, 16:04

Newcomer hat geschrieben:So hab ich das immer gemacht
Das sieht schwer umwegig aus. Zudem glaube ich nicht, dass dieser Code das tut, was du möchtest.

Von welcher Python-Version redest du eigentlich? Ich nehme an Python 3, da du nicht von object erben lässt. Korrekt?
Antworten