Hallo Ihr Pythonprofis,
ich lasse eine Klasse A von einer Klasse B erben. Dabei soll die Nutzung einer bestimmten Methode von B aus einer Instanz von A heraus zu einer Fehlermeldung führen. Also überschreibe ich die Methode in A und werfe darin eine exception? Oder gibt es da was rafinierteres?
Grüße und ein schönes Wochenende für Euch
Poseidonius
Methode einer Basisklasse "ausgrauen"
Das funktioniert, aber nur, solange der Benutzer der Klassen sich brav benimmt. Aber wer will, ruft halt
B.foo(self)
auf, statt
mein_a.foo()
womit dann deine Plaene ueber den Haufen sind.
Warum willst du das ueberhaupt machen? Ist die Methode ein Implementierungsdetail? Dann markier sie als private, mit einem, oder sogar vielleicht hier mal ausnahmsweise zwei Unterstrichen. Dann "darf" keiner sie benutzen.
Letztlich verhindern kann auch das es nicht, und darum wuerde ich es auch erst gar nicht versuchen.
B.foo(self)
auf, statt
mein_a.foo()
womit dann deine Plaene ueber den Haufen sind.
Warum willst du das ueberhaupt machen? Ist die Methode ein Implementierungsdetail? Dann markier sie als private, mit einem, oder sogar vielleicht hier mal ausnahmsweise zwei Unterstrichen. Dann "darf" keiner sie benutzen.
Letztlich verhindern kann auch das es nicht, und darum wuerde ich es auch erst gar nicht versuchen.
- pillmuncher
- User
- Beiträge: 1484
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@Poseidonius: Nein, das ist schon richtig so. Allerdings verstößt dieses Design gegen gegen das Liskov-Substitutions-Prinzip, da dessen zentrale Forderung, dass Unterklassen jederzeit für die Oberklasse substituiert werden können, bei dir nicht erfüllt ist. Wenn man zum Contract der Methode 'kann eine DarfNichtAufUnterklassenAufgerufenWerdenException werfen' hinzufügt, dann ist zwar das LSP formal wieder erfüllt, allerdings zu dem Preis, dass um jede Stelle im Client-Code, wo die Methode aufgerufen wird, ein try-except-Block gewickelt werden muss. Dann kann man auch gleich wieder von Hand dispatchen:
Explizites Type Dispatching, egal ob mittels if-Block oder try-except-Block, ist ziemlich un-OO. Ich würde ein anderes Design wählen. Beschreib doch mal, warum du das tun möchtest, vielleicht fällt jemandem ja eine andere Lösung ein.
Code: Alles auswählen
if type(x) is A:
x.method_which_can_only_be_invoked_on_base_class_instance()
In specifications, Murphy's Law supersedes Ohm's.
-
- User
- Beiträge: 63
- Registriert: Montag 23. Januar 2006, 08:58
Hallo deets, hallo pillmuncher,
danke für Eure raschen Anworten ! Offenbar ist mein Konzept falsch ...
Hinter der Basisklasse B versteckt sich eine Kommunikationsschnittstelle, die mit B.read() und B.write() zwei Funktionen implementiert. Von dieser Basisklasse sollen A und Z erben, wobei erstere eine Inputklasse und letztgenannte eine Ausgabeklasse darstellen sollen. Dabei wollte ich vermeiden, dass jemand mit einer Inputklasse alles durcheinander bringt und Daten versenden will, also ein A.write() aufruft. Die Klasse B soll im System die spezielle Funktionalität der Schnittstelle kapseln und austauschbar sein.
Grüße
Poseidonius
danke für Eure raschen Anworten ! Offenbar ist mein Konzept falsch ...
Hinter der Basisklasse B versteckt sich eine Kommunikationsschnittstelle, die mit B.read() und B.write() zwei Funktionen implementiert. Von dieser Basisklasse sollen A und Z erben, wobei erstere eine Inputklasse und letztgenannte eine Ausgabeklasse darstellen sollen. Dabei wollte ich vermeiden, dass jemand mit einer Inputklasse alles durcheinander bringt und Daten versenden will, also ein A.write() aufruft. Die Klasse B soll im System die spezielle Funktionalität der Schnittstelle kapseln und austauschbar sein.
Grüße
Poseidonius
dann waere die Loesung wohl eher zwei weitere Klassen anzubieten, welche einmal input und einmal output bereitstellen. So wie es zB in der Java-IO InputStream und OutputStream gibt. Oder das ganze durch Komposition zu erledigen.
- pillmuncher
- User
- Beiträge: 1484
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@Poseidonius: Gemäß dem Interface Segregation Principle sollte jeder Programmteil nur mit denjenigen Interfaces konfrontiert sein, die dort auch verwendet werden. Wenn eine aus einer Funktion heraus nur Daten gesendet werden sollen, dann sollte sie auch nur einen Writer (nennen wir ihn mal so) erwarten, selbst wenn sie tatsächlich einen Stream mir read() und write() Methoden übergeben bekommt, zB so:Entscheidend ist nicht, von welcher Klasse das übergebene Objekt tatsächlich ist, sondern dass dessen Klasse ein Subtyp der erwarteten ist. Stream-Klassen können wie oben auch dynamisch erzeugt werden und sind dann sowohl Reader als auch Writer.
Code: Alles auswählen
class Reader(object):
def read(self):
pass
class Writer(object):
def write(self, data):
pass
def combine(reader, writer):
class Stream(reader.__class__, writer.__class__):
read = reader.read
write = writer.write
return Stream()
#-----------------------------------------------------------------
def foo(reader):
print reader.read()
def bar(writer):
writer.write('hey joe!')
def reply_double(stream):
x = stream.read()
stream.write(x*2)
#-----------------------------------------------------------------
class MyReader(Reader):
def read(self):
''' spezielle MyReader-Implementierung '''
return 'hello'
class YourWriter(Writer):
def write(self, data):
''' spezielle YourWriter-Implementierung '''
print data
#-----------------------------------------------------------------
stream = combine(MyReader(), YourWriter())
foo(stream)
bar(stream)
reply_double(stream)
In specifications, Murphy's Law supersedes Ohm's.