Seite 1 von 1
Virtuelle methoden in Python?
Verfasst: Mittwoch 21. Januar 2009, 15:24
von madfrog
Gibt es in Python eine vergleichbare Struktur zu dem üblichen C++-Syntax: "virtual void draw()" um zu erzwingen, daß diese Methode von Subklassen implementiert werden muß?
Verfasst: Mittwoch 21. Januar 2009, 15:31
von Rebecca
Du kannst die Methode einen NotImplementedError werfen lassen.
Verfasst: Mittwoch 21. Januar 2009, 15:33
von DasIch
Mit
Abstact Base Classes erreichst du wohl am ehesten was du willst.
Verfasst: Mittwoch 21. Januar 2009, 15:58
von madfrog
Danke! Hier die erste Implementation die den Vorschlag von Rebecca umsetzt:
Code: Alles auswählen
class virtualmethod(object):
def __init__(self, func):
assert callable(func)
self._func = func
def __call__(self, *args, **kwargs):
msg = "Invalid call of %s (virtual method)." % self._func.__name__
raise NotImplementedError(msg) # --- verbessert
class VirtualDraw(object):
@virtualmethod
def draw(self):
pass
#draw = virtualmethod(draw) --- verbessert durch Dekorator
class ImplementedDraw(VirtualDraw):
def draw(self):
print "WORKS!"
test1 = VirtualDraw()
#test1 = ImplementedDraw()
test1.draw()
Leider komm ich nicht an den Namen der Klasse ran, wo die virtuelle Methode de facto noch nicht implementiert ist.
Verfasst: Mittwoch 21. Januar 2009, 16:21
von DasIch
Was spricht gegen
Code: Alles auswählen
class VirtualDraw(object):
def draw(self):
raise NotImplementedError
?
Außerdem würde man statt
Code: Alles auswählen
raise NotImplementedError, 'my cool message'
# folgendes machen
raise NotImplementedError('my cool message')
Außerdem Pythons
Dekoratoren kennst du?
Verfasst: Mittwoch 21. Januar 2009, 16:31
von madfrog
DasIch hat geschrieben:Was spricht gegen
Code: Alles auswählen
class VirtualDraw(object):
def draw(self):
raise NotImplementedError
?
Ich würde sagen im Sinne von Erweiterungen macht es Sinn flexibler auf solche Situationen reagieren zu könnnen, oder? So finde ich ja vielleicht noch nen Weg die Klasse rauszukriegen dessen virtuelle Methode aufgerufen wurde. Dann weiß ich besser wo ich schauen muß.
DasIch hat geschrieben:
Außerdem würde man statt
Code: Alles auswählen
raise NotImplementedError, 'my cool message'
# folgendes machen
raise NotImplementedError('my cool message')
Ja das ändere ich. Bin noch nicht so weit was Python angeht.
DasIch hat geschrieben:Außerdem Pythons
Dekoratoren kennst du?
Und das ist auch ein guter Tip. Dekoratoren habe ich gerade im abc Modul kennengelernt.
Hier die zweite Umsetzung mit abc:
Code: Alles auswählen
from abc import ABCMeta, abstractmethod
class VirtualDraw(object):
__metaclass__ = ABCMeta
@abstractmethod
def draw(self):
pass
class ImplementedDraw(VirtualDraw):
def draw(self):
print "WORKS!"
test1 = VirtualDraw()
#test1 = ImplementedDraw()
test1.draw()
Wobei ich hier nicht mal eine Instanz von VirtualDraw() erstellen kann, sondern er mir gleich nen Fehler wirft.
Kann mir noch wer sagen, wie Python den Aufruf der Klassenmethode genau aufruft? Wird bei instanz.funktion() direkt Klasse.funktion.__call__(instanz) aufgerufen oder passiert da mehr?
Verfasst: Mittwoch 21. Januar 2009, 17:29
von Darii
madfrog hat geschrieben:Kann mir noch wer sagen, wie Python den Aufruf der Klassenmethode genau aufruft? Wird bei instanz.funktion() direkt Klasse.funktion.__call__(instanz) aufgerufen oder passiert da mehr?
Da Pyton zur Kompilierzeit keine Informationen über die Datenstrukturen hat kann Klasse.funktion im Allgemeinen nicht direkt aufgerufen werden. Es wird also zunächst ein ``gettattr(instanz, "funktion")`` ausgeführt, das im Falle einer Methode eine "bound method" zurückgibt(sowas wie ``functools.partial(Klasse.funktion, instanz)``. Diese wird dann wie eine normale Funktion aufgerufen.
Wenn du dich für solche Internas interessierst, hilft dir vielleicht das Modul "dis" weiter.
Code: Alles auswählen
In [46]: def foo(): return instanz.funktion()
....:
In [47]: dis.dis(foo)
1 0 LOAD_GLOBAL 0 (instanz)
3 LOAD_ATTR 1 (funktion)
6 CALL_FUNCTION 0
9 RETURN_VALUE