Seite 1 von 2

Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 19:15
von mutetella
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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 19:29
von cofi
Nicht im Python-Objekt-Modell. Du kannst aber die Methode ueberschreiben und in der Ueberschreibenden explizit die Ueberschriebene aufrufen.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 19:37
von EyDu
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.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 19:40
von DasIch
Du könntest die überschriebene Methode, in einer Metaklasse, mit einer Methode ersetzten die tut was du willst.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 20:00
von mutetella
@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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Dienstag 19. Juli 2011, 20:10
von cofi
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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 11:23
von mutetella
@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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 11:34
von 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.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 11:40
von 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/

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 12:50
von cofi
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.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 13:42
von mutetella
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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 13:49
von cofi
`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 ;)

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 13:51
von 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()


Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 15:53
von Newcomer
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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:04
von /me
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?

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:08
von 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.

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:12
von /me
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().

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:19
von cofi
@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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:29
von Newcomer
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

Re: Lässt sich eine vererbte Methode erweitern?

Verfasst: Mittwoch 20. Juli 2011, 16:36
von Newcomer
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
>>>