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.
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: 3554
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
>>> 
deets

/me hat geschrieben: 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().
Jein. Fuer Konstruktoren - ich natuerlich auch. Wobei man da gerne Fehler macht, weil man in zwei Basisklassen, die direkt von object erben oft vergisst.

Aber bei normalen Methoden ist die Sache nicht gaaanz so einfach - denn da erwarte ich ja genau einen Rueckgabewert, und im Fall eines Diamondshape kommt's auf die MRO an- da gehe ich dann doch gelegentlich hin, und mach's explizit.

Ganz ehrlicherweise ist aber das ganze Problem eh eher gering, das ich mal mehr als 1 Ableitungshierarchie habe kommt *so* selten vor, und Mehrfachvererbung eigentlich immer nur als Pseudo-Mixins.

Code: Alles auswählen



class B(object):

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


    def foo(self):
        return "B"

class C(object):

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


    def foo(self):
        return "C"


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__"



    def foo(self):
        return super(D, self).foo()

class E(C, B):

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



    def foo(self):
        return super(E, self).foo()

d = D()
print d.foo()

e = E()
print e.foo()
deets

Newcomer hat geschrieben:Mir macht das nur spaß, deshalb reicht es für meine verhältnisse
Bloss weil man nicht Installateur werden will, muss man sich mit einer Bohrmaschine ja noch lange nicht in den Fuss bohren, oder? Sich gutes arbeiten anzugewoehnen ist immer ... gut. Dass du es bisher so gemacht hast ist ja ok. Sich aber dem dazulernen zu verweigern, finde ich spannend.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Newcomer hat geschrieben:bei mir funktioniert das einwandfrei:
Dein Code ja, weil `Mutter.hello` bei dir keine wirkliche Methode ist und `self` nicht nutzt. Eine Methode benoetigt als erstes Argument aber _immer_ ein Instanz-Objekt und keine String namens "dummy".
`Tochter.hello` sollte also `self.hello_ur(self, "none")` nutzen.

In Python3 gibt das eine andere Fehlermeldung, hat aber denselben Grund:

Code: Alles auswählen

%> python3 -i tmp/foo.py
>>> t = Tochter()
>>> t.hello("there")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tmp/foo.py", line 13, in hello
    self.hello_ur("dummy", "none")
  File "tmp/foo.py", line 6, in hello
    print(self.greeter % text)
AttributeError: 'str' object has no attribute 'greeter'
>>>
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

@cofi Hä das versteh ich nicht, bei mir funktionierts auch mit self, is aber auch egal. P.s. Ist dein Bild aus conway´s game of life?
@deets
deets hat geschrieben:
Newcomer hat geschrieben:Mir macht das nur spaß, deshalb reicht es für meine verhältnisse
Bloss weil man nicht Installateur werden will, muss man sich mit einer Bohrmaschine ja noch lange nicht in den Fuss bohren, oder? Sich gutes arbeiten anzugewoehnen ist immer ... gut. Dass du es bisher so gemacht hast ist ja ok. Sich aber dem dazulernen zu verweigern, finde ich spannend.
Ich hab nicht behauptet, dass ich nichts gelernt habe oder lernen wollte. Hab mir die bisherigen beiträge alle brav durchgelesen und vieles neues dazugelernt (super() bsp). Dein Vergleich mit dem Installateur zieht nicht so ganz, denn unter berücksichtigung einer Verletzung am Fuß müsste mindestens mein Prozesser in die Luft gehen, wenn ich ihn zu sehr mit schlechtem code ärgere (-; 8) 8) :D
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ok, ich hab' jetzt mal a bisl rumgebastelt und -gespielt und bin zu folgender Erkenntnis gelangt :) :
  • Durch den Einsatz von 'super()' wird bei Mehrfachvererbung in der korrekten Reihenfolge und Anzahl instanziiert:
    Erst mal die Mama, danach, in umgekehrter Reihenfolge die Klassen, von denen direkt geerbt wird.
  • Dazu muss aber konsequent in allen beteiligten Klassen 'super()' verwendet werden.
  • Bei einfacher Vererbung kann und sollte auf 'super()' verzichtet werden, siehe BlackJack's Link.
Stimmt das so in etwa?

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:
  • Durch den Einsatz von 'super()' wird bei Mehrfachvererbung in der korrekten Reihenfolge und Anzahl instanziiert:
    Erst mal die Mama, danach, in umgekehrter Reihenfolge die Klassen, von denen direkt geerbt wird.
Es ist leider ein ganzes Stueck komplizierter. Die Details gibts hier: http://www.python.org/download/releases/2.3/mro/

Oh und dann schiess ich nochmal gegen `super` ist boese: http://rhettinger.wordpress.com/2011/05 ... red-super/
BlackJack

@cofi: Hettinger ist ja schon irgendwo ein Held, aber bei dem Artikel habe ich nur den Kopf geschüttelt. `super()` ist gar nicht so schlimm, wenn man bei jeder Methode sowohl in der Signatur als auch beim Aufruf ein ``**kwds`` anhängt und nette kleine Adapterklassen für alles schreibt was nicht `super()` verwendet. :?

Ich sehe zwei Probleme bei `super()`.

1. Man kann herkömmlichen und `super()`-Code nicht mischen. Das heisst ein Umstieg ist mit umschreiben von vielen Klassen und mit Adapterklassen für Code den man nicht unter seiner Kontrolle hat, verbunden. Da ich Mehrfachvererbung nur für Mixin-Klassen verwende, sehe ich dabei für mich aber überhaupt keinen Vorteil `super()` zu verwenden und mir die zusätzliche Arbeit zu machen. Und ich glaube nicht das ich damit alleine da stehe.

2. Vielleicht nur mein Problem, aber ich sehe das durchreichen von ``**kwds`` überall als ziemlich unschön an. Das riecht nach Boilerplate oder Hack und das die Sprache an der Stelle nicht ordentlich durchdacht ist. Beim Vorbild für die MRO gibt es das nicht, weil die Funktionssignatur bei Dylan in der gesamten Hierarchie tatsächlich fest sein muss.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich hätte jetzt mal ein konkretes Beispiel:

Code: Alles auswählen

class DefaultList(list):
    def __init__(self, iterable=None, default=None):
        self._default = default or ()
        list.__init__(self, iterable or ())

    def remove(self, value):
        list.remove(self, value)
        self._is_empty()

    def pop(self, index):
        list.pop(self, index)
        self._is_empty()
   
    def __delitem__(self, index):
        list.__delitem__(self, index)
        self._is_empty()

    def __delslice__(self, start, stop):
        list.__delslice__(self, start, stop)
        self._is_empty()
 
    def _is_empty(self):
        if len(self) <= 0:
            self[:] = self._default
Sehe ich das richtig, dass hier der explizite Aufruf von list-Methoden sinnvoller oder sogar richtiger als der Einsatz von 'super()' ist?

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