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: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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

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: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

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

@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
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@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

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

@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
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

`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

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

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: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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?
deets

@Newcomer

Und was ist daran in irgendeiner Art von Vorteil gegenueber wahlweise super? Beziehungsweise

Code: Alles auswählen

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

class Tochter(Mutter):
      

    def hello(self,text):
        Mutter.hello(self, "dummy")
        print(text)
wenn man nicht super verwenden mag oder kann?

Ich kann auf jedenfall schonmal aufzaehlen, warum es *nicht* besser ist:

- mehr nutzloser code, ich kann mir ja zB das __init__ sparen in Tochter
- eine Instanzvariable weniger - weniger Zustand ist *immer* besser.
- Man "macht" es halt so nicht, ist un-pythonisch, das Idiom ist ja zB auch und gerade im Konstruktor

Code: Alles auswählen


def __init__(self):
     SuperKlasse.__init__(self)

wenn man schon auf super verzichtet.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

deets hat geschrieben:

Code: Alles auswählen

def __init__(self):
     SuperKlasse.__init__(self)
wenn man schon auf super verzichtet.
Dann sollte man allerdings auch einheitlich im kompletten Projekt dabeibleiben. Diese klassische Art und die Verwendung von super innerhalb einer Vererbungshierarchie beißen sich ziemlich böse.

Ich persönlich verwende durchgängig super().
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

@Newcome: Nein, das ist absolut kaputt.
Siehe hier:

Code: Alles auswählen

In [6]: %edit tmp/foo.py
Editing... > emacsclient -nw +0 tmp/foo.py
done. Executing edited code...

In [7]: t = Tochter()

In [8]: t.hello("there")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/cofi/<ipython console> in <module>()

/home/cofi/tmp/foo.py in hello(self, text)
     10         self.hello_ur = Mutter.hello
     11 
     12     def hello(self, text):
---> 13         self.hello_ur("dummy", "none")
     14         print text

TypeError: unbound method hello() must be called with Mutter instance as first argument (got str instance instead)

Code: Alles auswählen

# tmp/foo.py
class Mutter(object):
    def __init__(self):
        self.greeter = "Hello %s"

    def hello(self, text):
        print self.greeter % text

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

    def hello(self, text):
        self.hello_ur("dummy", "none")
        print text
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

ja, die neueste version. Und bei mir hat das immer geklappt, ich will ja auch net python bzw Informatik studieren oder programmierer werden. Mir macht das nur spaß, deshalb reicht es für meine verhältnisse
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

bei mir funktioniert das einwandfrei:

Code: Alles auswählen

Python 3.2.1 (default, Jul 10 2011, 20:02:51) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
>>> t=Tochter()
>>> t.hello("there")
none
there
>>> 
Antworten