Virtuelle methoden in Python?

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.
Antworten
madfrog
User
Beiträge: 19
Registriert: Montag 19. Januar 2009, 01:56

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ß?
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Du kannst die Methode einen NotImplementedError werfen lassen.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Mit Abstact Base Classes erreichst du wohl am ehesten was du willst.
madfrog
User
Beiträge: 19
Registriert: Montag 19. Januar 2009, 01:56

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.
Zuletzt geändert von madfrog am Mittwoch 21. Januar 2009, 16:42, insgesamt 1-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

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?
madfrog
User
Beiträge: 19
Registriert: Montag 19. Januar 2009, 01:56

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?
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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        
Antworten