Gibt es in Python eine Möglichkeit, eine ungebundene Methode zu binden, ohne sie aufzurufen?
Ich schreibe ein wxPython-Programm und für eine bestimmte Klasse habe ich beschlossen, dass es schön wäre, die Daten aller meiner Schaltflächen als Liste von Tupeln auf Klassenebene zu gruppieren, wie folgt:
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
ungebundenen Methoden sind, explodiert mein Programm in einem spektakulären Feuer und ich weine.
Ich habe mich online nach einer Lösung umgesehen, die ein relativ einfaches, lösbares Problem sein sollte. Leider konnte ich nichts finden. Im Moment arbeite ich daran functools.partial
, das zu umgehen, aber weiß jemand, ob es eine saubere, gesunde und pythonische Methode gibt, eine ungebundene Methode an eine Instanz zu binden und sie weiterzugeben, ohne sie aufzurufen?
Antworten:
Alle Funktionen sind auch Deskriptoren , sodass Sie sie durch Aufrufen ihrer
__get__
Methode binden können :Hier ist R. Hettingers exzellenter Leitfaden für Deskriptoren.
Als eigenständiges Beispiel aus Keiths Kommentar :
quelle
MethodType
einen, weil sie in py3k genauso funktioniert, währendMethodType
die Argumente ein wenig geändert wurden.bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))
Beispiel:class A: pass;
a = A();
bind(a, bind, 'bind')
__get__
dies implizit aus dem Objektparameter übernommen wird. Ich bin mir nicht einmal sicher, ob die Bereitstellung etwas bewirkt, da es keinen Unterschied macht, welchen Typ ich als zweiten Parameter anbiete, unabhängig davon, wofür der erste Parameter eine Instanz ist. Alsobind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))
sollte der Trick auch tun. (Ich würde es zwar vorziehenbind
, persönlich als Dekorateur verwendet zu werden, aber das ist eine andere Sache.)__dict__
Attributzugriff erhalten Sie ungebundene oder gebundene Methoden über das normale Deskriptorprotokoll. Ich habe immer angenommen, dass es eine Art Magie war, die währendtype.__new__()
Dies kann sauber mit types.MethodType erfolgen . Beispiel:
quelle
__get__
). Ich weiß nicht, für welche Python-Version Sie dies getestet haben, aber für Python 3.4 akzeptiert dieMethodType
Funktion zwei Argumente. Die Funktion und die Instanz. Das sollte also geändert werden intypes.MethodType(f, C())
.wgt.flush = types.MethodType(lambda self: None, wgt)
Das Erstellen eines Abschlusses mit Selbst darin bindet die Funktion technisch nicht, ist jedoch eine alternative Methode zur Lösung des gleichen (oder sehr ähnlichen) zugrunde liegenden Problems. Hier ist ein triviales Beispiel:
quelle
functools.partial(handler, self)
Dies wird binden
self
anhandler
:Dies funktioniert, indem
self
als erstes Argument an die Funktion übergeben wird.object.function()
ist nur syntaktischer Zucker fürfunction(object)
.quelle
functools.partial
ein Lambda verwenden, anstatt es zu definieren. Es löst jedoch nicht das genaue Problem. Sie haben es immer noch mit einemfunction
statt mit einem zu tuninstancemethod
.function
dessen erstes Argument Sie teilweise bearbeitet haben, undinstancemethod
; Ententippen kann den Unterschied nicht erkennen.functools.partial
löscht einige Metadaten, z__module__
. (Außerdem möchte ich für die Aufzeichnung angeben, dass ich sehr erschrecke, wenn ich mir meinen ersten Kommentar zu dieser Antwort ansehe.) Tatsächlich erwähne ich in meiner Frage, dass ich sie bereits verwende,functools.partial
aber ich hatte das Gefühl, dass es einen "reineren" Weg geben muss. da es einfach ist, sowohl ungebundene als auch gebundene Methoden zu erhalten.Spät zur Party, aber ich kam mit einer ähnlichen Frage hierher: Ich habe eine Klassenmethode und eine Instanz und möchte die Instanz auf die Methode anwenden.
Auf die Gefahr hin, die Frage des OP zu vereinfachen, habe ich etwas weniger Geheimnisvolles getan, das für andere, die hier ankommen, nützlich sein kann (Einschränkung: Ich arbeite in Python 3 - YMMV).
Betrachten Sie diese einfache Klasse:
Folgendes können Sie damit tun:
quelle
meth
aufrufbar sein, ohne ihm dasa
Argument senden zu müssen (weshalb ich es ursprünglich verwendet habefunctools.partial
) -, aber dies ist vorzuziehen, wenn Sie die Methode nicht weitergeben müssen und es einfach können Rufen Sie es sofort auf. Auch dies funktioniert in Python 2 genauso wie in Python 3.