Verständnisfrage: Subclass überschreibt Methode nicht

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.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Freitag 24. September 2010, 21:56

Habe Klasse `Man` mit Methode `speak`, die in der Subclass `ShoutingMan` überschrieben werden soll. Es gibt zusätzlich die Standard-Methode zum Sprechen `__default_speak`. Die brauche ich, um abfragen zu können, ob jemand anders als normal spricht, also ob `speak` überschrieben wurde. Daher setze ich in `Man.__init__` einfach `self.speak = self.__default_speak`.

Problem: in Subclasses wird eine Methode namens `speak` ignoriert, d.h. die überschreibt den in der Baseclass in `__init__` zugewiesenen Wert nicht. Ich kann mir derzeit nur behelfen, indem ich `__init__` re-definiere und manuell `speak` neu mittels einer Hilfsfunktion zuweise, siehe Code unten.

Wieso? Der Umweg über die Hilfsfunktion `yell` und die Re-Definition von `__init__` ist eigentlich unnötig und daher unschön. V.a. aber verstehe ich es nicht...

Code: Alles auswählen

class Man:
    def __init__(self):
        self.speak = self.__default_speak
    def __default_speak(self, text):
        return text
    def speaks_special(self):
        return not self.speak == self.__default_speak

class ShoutingMan(Man):
    def speak(self, text):
        return text.upper()

sman = Man()
print sman.speak("Hello")
# prints: Hello, `speak` seems not to be overwritten in class `ShoutingMan`
print sman.speaks_special()
# prints: False

class YellingMan(Man):
    def __init__(self):
        Man.__init__(self)
        self.speak = self.yell
    def yell(self, text):
        return text.upper()

yman = YellingMan()
print yman.speak("Hello")
# prints: HELLO, `speak` has been overwritten in class `YellingMan`
print yman.speaks_special()
# prints: True
BlackJack

Freitag 24. September 2010, 22:12

@droptix: `ShoutingMan` hat zwar eine eigene `speak()`-Methode, die wird aber im erstellten Exemplar wenn die `__init__()` ausgeführt wird, mit `self.__default_speak` verdeckt. Die `__init__()` von `Man` wird doch beim erstellen von `ShoutingMan` ausgeführt, wenn `ShoutingMan` keine eigene `__init__()` definiert.
ntrunk
User
Beiträge: 83
Registriert: Sonntag 7. September 2008, 23:09
Wohnort: Buchen (Odenwald)

Samstag 25. September 2010, 06:59

@droptix: Ergänzend zu der Erklärung von Blackjack möchte ich mal etwas provokant in den Raum werfen: Wozu willst du das eigentlich wissen? Ich hatte auch schon ähnliche Situationen, und es hat sich immer herausgestellt, dass die Ursache in einem suboptimalen Design des Klassenmodells lag. Wenn dabei dann ungefähr solcher Code herauskommt:

Code: Alles auswählen

class BaseClass:
[...]
    def tu_was(self):
        if self.was_overwritten('some_method'):
            self.tu_dies()
        else:
            self.tu_jenes()
dann wird das wahrscheinlich besser gelöst in einem Überschreiben von tu_jenes(). Überleg mal:
- wenn der Code in Man die Spezialfälle abhandeln soll, für was brauchst du dann überhaupt die abgeleitete Klasse?
- sobald du eine neue Klasse ableitest, musst du die Basisklasse verändern (und neu testen und alle bereits funktionierenden abgeleiteten Klassen ebenfalls...)!
Der Sinn des Überschreibens von Methoden liegt ja m.E. gerade darin, dass man den Code für die Basisklasse völlig von den abgeleiteten Klassen abkoppeln kann.

Gruß
Norbert
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Samstag 25. September 2010, 07:36

droptix hat geschrieben:Habe Klasse `Man` mit Methode `speak`, die in der Subclass `ShoutingMan` überschrieben werden soll. Es gibt zusätzlich die Standard-Methode zum Sprechen `__default_speak`. Die brauche ich, um abfragen zu können, ob jemand anders als normal spricht, also ob `speak` überschrieben wurde. Daher setze ich in `Man.__init__` einfach `self.speak = self.__default_speak`.
Du kannst auch Zuweisungen in der Klassendefinition selbst machen:

Code: Alles auswählen

class Man(object):
    def __speak(): pass
    speak = __speak
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Samstag 25. September 2010, 08:51

Darii hat geschrieben:Du kannst auch Zuweisungen in der Klassendefinition selbst machen:

Code: Alles auswählen

class Man(object):
    def __speak(): pass
    speak = __speak
Aber bitte ohne doppelte führende Unterstriche..
the more they change the more they stay the same
BlackJack

Samstag 25. September 2010, 09:15

Mal abgesehen davon, dass ich es wie ntrunk sehe und das wahrscheinlich gar nicht machen würde, täte ich es komplett ohne extra Methode. Man könnte einfach die "aktuelle" Methode mit der in `Man` vergleichen:

Code: Alles auswählen

class Man(object):
    def speak(self, text):
        return text
    
    def is_special_speaker(self):
        return self.__class__.speak != Man.speak


class ShoutingMan(Man):
    def speak(self, text):
        return text.upper()


def main():
    for man in [Man(), ShoutingMan()]:
        print man.speak('Hello')
        print man.is_special_speaker()
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Samstag 25. September 2010, 09:33

Ich find das mit einem "property" schöner und passender:

Code: Alles auswählen

class Man(object):
    def speak(self, text):
        return text
   
    @property
    def is_special_speaker(self):
        return self.__class__.speak != Man.speak
the more they change the more they stay the same
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Samstag 25. September 2010, 09:36

Dav1d hat geschrieben:
Darii hat geschrieben:Du kannst auch Zuweisungen in der Klassendefinition selbst machen:

Code: Alles auswählen

class Man(object):
    def __speak(): pass
    speak = __speak
Aber bitte ohne doppelte führende Unterstriche..
Weil? Genau für so einen Fall sind sie da, um versehentliches Überschreiben zu verhindern.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Samstag 25. September 2010, 09:54

Ich finde man sollte sie sogar überschreiben:

Code: Alles auswählen

class Man(object):
    def __init__(self):
        pass # was auch immer hier noch hin soll
    def speak(self, text):
        return text
    def speaks_special(self):
        return not self.__class__.speak == Man.speak

class ShoutingMan(Man):
    def __init__(self):
        Man.__init__(self)
    def speak(self, text):
        return text.upper()
the more they change the more they stay the same
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Samstag 25. September 2010, 11:07

ntrunk hat geschrieben:@droptix: Ergänzend zu der Erklärung von Blackjack möchte ich mal etwas provokant in den Raum werfen: Wozu willst du das eigentlich wissen? Ich hatte auch schon ähnliche Situationen, und es hat sich immer herausgestellt, dass die Ursache in einem suboptimalen Design des Klassenmodells lag.
Ja, das kann gut sein, daher frag ich ja. Das hier gefällt mir ganz gut, darauf bin ich nicht gekommen:
BlackJack hat geschrieben:Mal abgesehen davon, dass ich es wie ntrunk sehe und das wahrscheinlich gar nicht machen würde, täte ich es komplett ohne extra Methode. Man könnte einfach die "aktuelle" Methode mit der in `Man` vergleichen:

Code: Alles auswählen

class Man(object):
    def speak(self, text):
        return text
    
    def is_special_speaker(self):
        return self.__class__.speak != Man.speak
@Dav1d: Was hat das mit dem @property auf sich? Ist das Python 3000?
BlackJack

Samstag 25. September 2010, 11:16

@droptix: Die `property()`-Funktion gibt es sein Python 2.2 und die Decorator-Syntax (@) seit Python 2.4 -- ist also beides schon recht "alt".
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Samstag 25. September 2010, 11:35

Dav1d hat geschrieben:Ich finde man sollte sie sogar überschreiben:
Was meinst du? Was willst du überschreiben? Man.speak offensichtlich nicht. Ich auch nicht, deswegen __speak(bzw. __default_speak wie auch immer man das nennen möchte).
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Samstag 25. September 2010, 11:39

Darii hat geschrieben:
Dav1d hat geschrieben:Ich finde man sollte sie sogar überschreiben:
Was meinst du? Was willst du überschreiben? Man.speak offensichtlich nicht. Ich auch nicht, deswegen __speak(bzw. __default_speak wie auch immer man das nennen möchte).
:?:

Ich überschreibe Man.speak mit ShoutingMan.speak
the more they change the more they stay the same
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Samstag 25. September 2010, 12:02

Dav1d hat geschrieben:Ich überschreibe Man.speak mit ShoutingMan.speak
Ahhh… du überschreibst niemals Man.speak. Das ist doch der Punkt. Genausowenig wie self._Man__speak jemals überschrieben wird.
BlackJack

Samstag 25. September 2010, 12:25

@Darii: Also ich kenne das Neuimplementieren einer Methode in einer Unterklasse als "überschreiben". Und genau das tut Dav1d in seinem Beispiel.
Antworten