2009-06-18 6 views
92

Gibt es in Python eine Möglichkeit, eine ungebundene Methode zu binden, ohne sie aufzurufen?Python: Eine ungebundene Methode binden?

ich ein WxPython Programm schreibe, und für eine bestimmte Klasse I entschieden, würde die Daten aller meiner Tasten zusammen als eine Klasse-Ebene-Liste von Tupeln Gruppe schön sein, etwa so:

class MyWidget(wx.Window): 
    buttons = [("OK", OnOK), 
       ("Cancel", OnCancel)] 

    # ... 

    def Setup(self): 
     for text, handler in MyWidget.buttons: 

      # This following line is the problem line. 
      b = wx.Button(parent, label=text).Bind(wx.EVT_BUTTON, handler) 

Das Problem ist, da alle Werte von handler sind ungebundene Methoden, explodiert mein Programm in einem spektakulären Brand und ich weine.

Ich suchte online nach einer Lösung für was scheint, sollte ein relativ direktes, lösbares Problem sein. Leider konnte ich nichts finden. Im Moment verwende ich functools.partial, um dies zu umgehen, aber weiß jemand, ob es eine saubere, gesunde, pythonische Methode gibt, eine ungebundene Methode an eine Instanz zu binden und sie weiterzugeben, ohne sie aufzurufen?

+1

definieren „ungebundene Methode“ – Christopher

+4

@Christopher - Ein Verfahren, das nicht auf den Umfang des Objekts gebunden ist es aus gesaugt wurde, so dass Sie sich selbst explizit zu übergeben. –

Antwort

143

Alle Funktionen sind auch Deskriptoren, so dass Sie sie durch den Aufruf ihrer __get__ Methode binden können:

bound_handler = handler.__get__(self, MyWidget) 

Hier R. Hettinger ausgezeichneten guide zu Deskriptoren.

+2

Das ist ziemlich cool. Ich mag, wie Sie den Typ weglassen können und stattdessen eine "gebundene Methode? .f" zurückbekommen. – Kiv

+0

Ich mag diese Lösung über 'MethodType', weil sie in py3k gleich funktioniert, während die' MethodType' Argumente ein wenig verändert wurden. – bgw

+8

Und damit eine Funktion zum Binden von Funktionen an Klasseninstanzen: 'bind = Lambda-Instanz, func, asname: setattr (Instanz, asname, func .__ get __ (Instanz, Instanz .__ Klasse __))' Beispiel: 'Klasse A: pass; ' ' a = A(); ' ' binden (a, binden, 'binden') ' –

3

binden Diese self-handler:

bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs) 

Dies wird durch self als erstes Argument an die Funktion übergeben funktioniert. object.function() ist nur syntaktischer Zucker für function(object).

+1

Ja, aber das ruft die Methode auf. Das Problem ist, dass ich in der Lage bin, die gebundene Methode als aufrufbares Objekt zu übergeben. Ich habe die ungebundene Methode und die Instanz, an die ich möchte, dass es gebunden wird, aber kann nicht herausfinden, wie man alles zusammensetzt, ohne es sofort zu nennen –

+6

Nein, es wird nicht nur die Methode aufrufen, wenn Sie mach bound_handler(). Das Definieren eines Lambda ruft das Lambda nicht auf. –

+0

Sie könnten 'functools.partial' tatsächlich verwenden, anstatt ein Lambda zu definieren. Es löst das genaue Problem jedoch nicht. Sie haben es immer noch mit einer 'Funktion' anstatt einer' Instanzmethode' zu ​​tun. –

68

Dies kann sauber mit types.MethodType durchgeführt werden. Beispiel:

import types 

def f(self): print self 

class C(object): pass 

meth = types.MethodType(f, C(), C) # Bind f to an instance of C 
print meth # prints <bound method C.f of <__main__.C object at 0x01255E90>> 
+8

+1 Das ist großartig, aber es gibt keinen Hinweis darauf in den Python-Dokumenten unter der von Ihnen angegebenen URL. –

+2

+1, ich bevorzuge keine Aufrufe an magische Funktionen in meinem Code (d. H. '__get__'). Ich weiß nicht, für welche Version von Python Sie das getestet haben, aber in Python 3.4 nimmt die 'MethodType'-Funktion zwei Argumente an. Die Funktion und die Instanz. Dies sollte in 'types.MethodType (f, C())' geändert werden. –

+0

Hier ist es! Es ist ein guter Weg, Instanzmethoden zu patchen: 'wgt.flush = types.MethodType (Lambda-Selbst: None, wgt)' – Winand

7

Das Erstellen einer Closure mit self wird die Funktion technisch nicht binden, aber es ist eine alternative Möglichkeit, das gleiche (oder sehr ähnliche) zugrundeliegende Problem zu lösen. Hier ist ein einfaches Beispiel:

self.method = (lambda self: lambda args: self.do(args))(self) 
Verwandte Themen