2013-04-26 8 views
5

Warum ist es, dass in dem folgenden Code, eine Klassenvariable als Methode Zeigern Ergebnisse in ungebundenen Methodenfehlern verwenden, während eine gewöhnliche Variable funktioniert gut:Zeiger auf statische Methoden in Python

class Cmd: 
    cmd = None 

    @staticmethod 
    def cmdOne(): 
     print 'cmd one' 

    @staticmethod 
    def cmdTwo(): 
     print 'cmd two' 

def main(): 
    cmd = Cmd.cmdOne 
    cmd() # works fine 

    Cmd.cmd = Cmd.cmdOne   
    Cmd.cmd() # unbound error !! 

if __name__=="__main__": 
    main() 

Der vollständigen Fehler :

TypeError: unbound method cmdOne() must be called with Cmd instance as 
      first argument (got nothing instead) 

Antwort

2

Ich möchte dieses Verhalten von "unten" zu sehen.

Eine Funktion in Python fungiert als "descriptor object". Als solche hat es eine __get__() Methode.

Ein Lesezugriff auf ein Klassenattribut, das eine solche __get__()-Methode hat, wird auf diese Methode "umgeleitet". Ein Attributzugriff auf die Klasse wird als attribute.__get__(None, containing_class) ausgeführt, während ein Attributzugriff auf die Instanz auf attribute.__get__(instance, containing_class) abgebildet wird.

__get__() Ein Verfahren Aufgabe der Funktion ist die Funktion in einem Verfahren, das Gegenstand einzuwickeln, die die Parameter self wickelt weg - für den Fall eines Attributs Zugriff auf die Instanz. Dies wird als gebundene Methode bezeichnet.

Auf einem Klassenattribut Zugriff auf 2.x, ein __get__() kehrt ein ungebundenes Methode Wrapper die Funktion, während, wie ich learned today, auf 3.x, es selbst zurückgibt. (Beachten Sie, dass der Mechanismus __get__() in 3.x noch vorhanden ist, aber eine Funktion nur selbst zurückgibt.) Das ist fast das Gleiche, wenn man sich ansieht, wie es heißt, aber ein ungebundener Methodenwrapper prüft zusätzlich den korrekten Typ des self Arguments.

Ein Aufruf staticmethod() Aufruf erstellt nur ein Objekt, dessen __get__() Aufruf entwickelt wurde, um das ursprünglich angegebene Objekt zurückzugeben, so dass es das beschriebene Verhalten rückgängig macht. Das ist, wie HYRY's trick Werke: das Attribut acces rückgängig macht die staticmethod() Verpackung, der Anruf es wieder tut, so dass das „neue“ Attribut den gleichen Status wie die alten hat, obwohl in diesem Fall staticmethod() scheint zweimal angewandt werden (aber wirklich isn‘ t).

(BTW: Es funktioniert auch in diesem seltsamen Zusammenhang.

s = staticmethod(8) 
t = s.__get__(None, 2) # gives 8 

obwohl 8 ist keine Funktion und 2 ist keine Klasse)

In Ihrer Frage, zwei Fälle:

cmd = Cmd.cmdOne 
cmd() # works fine 

greift auf die Klasse und fragt nach seinem cmdOne Attribute, ein staticmethod() Objekt. Dieser wird über seine __get__() abgefragt und gibt die ursprüngliche Funktion zurück, die dann aufgerufen wird. Deshalb funktioniert es gut.

Cmd.cmd = Cmd.cmdOne 
Cmd.cmd() # unbound error 

macht das gleiche, aber ordnet dann diese Funktion zu Cmd.cmd. Die nächste Zeile ist ein Attribut Zugriff - das tut, wieder, den __get__() Aufruf die Funktion selbst und kehrt somit ein ungebundenes Verfahren, die mit einem korrekten self Objekt als erstes Argument aufgerufen werden muss.

3

Sie müssen staticmethod() verwenden, um die Funktion zu konvertieren:

Cmd.cmd = staticmethod(Cmd.cmdOne) 
+0

Beantwortet keine Frage. – martineau

3

Y Sie kommen in Python 2.x in das Verhalten von "ungebundenen Methoden". Wenn Sie in Python 2.x ein Attribut einer Klasse erhalten (z. B. in diesem Fall Cmd.cmd) und der Wert eine Funktion ist, dann "kapselt" die Klasse die Funktion in ein spezielles Objekt "ungebundene Methode", weil sie dies tut nehme an, dass Attribute von Klassen, die Funktionen sind und nicht mit staticmethod oder dekoriert sind, als Instanzmethoden gemeint sind (in diesem Fall eine falsche Annahme). Diese ungebundene Methode erwartet beim Aufruf ein Argument, obwohl die zugrunde liegende Funktion in diesem Fall kein Argument erwartet.

Dieses Verhalten wird in der language reference erläutert:

(in "Klassen" -Abschnitt)

When a class attribute reference (for class C, say) would yield a user-defined function object or [...], it is transformed into an unbound user-defined method object whose im_class attribute is C.

(in der "Benutzer-definierte Methoden" -Abschnitt)

When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.

[ ...]

When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.

Das verursacht den Fehler, den Sie sehen.

Sie könnten explizit die zugrunde liegende Funktion aus dem Verfahren Objekt abrufen und so nennen (aber es ist natürlich nicht ideal zu müssen, dies zu tun):

Cmd.cmd.im_func() 

Beachten Sie, dass Python 3.x got rid of unbound methods und Ihr Code würde läuft gut auf Python 3.x

+0

Um genau zu sein, ist es die Funktion selbst, die sich selbst in ein Objekt der "ungebundenen Methode" "umschließt": indem sie darauf zugreift, ruft die Klasse die Funktion '__get __()' auf, die den Wrapper erzeugt. – glglgl