En Python, existe-t-il un moyen de lier une méthode indépendante sans l'appeler?
J'écris un programme wxPython, et pour une certaine classe, j'ai décidé que ce serait bien de regrouper les données de tous mes boutons sous forme de liste de tuples au niveau de la classe, comme ceci:
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)
Le problème est que, puisque toutes les valeurs de handler
sont des méthodes non liées, mon programme explose dans une flambée spectaculaire et je pleure.
Je cherchais en ligne une solution à ce qui semble être un problème relativement simple et résoluble. Malheureusement, je n'ai rien trouvé. En ce moment, j'utilise functools.partial
pour contourner ce problème, mais est-ce que quelqu'un sait s'il existe une manière pythonique, saine et propre de lier une méthode non liée à une instance et de continuer à la transmettre sans l'appeler?
Réponses:
Toutes les fonctions sont également des descripteurs , vous pouvez donc les lier en appelant leur
__get__
méthode:Voici l'excellent guide des descripteurs de R. Hettinger .
En tant qu'exemple autonome tiré du commentaire de Keith :
la source
MethodType
celle, car elle fonctionne de la même façon dans py3k, alors queMethodType
les arguments de ont été légèrement modifiés.bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))
Exemple:class A: pass;
a = A();
bind(a, bind, 'bind')
__get__
cela prendra implicitement le paramètre object. Je ne sais même pas si le fournir fait quelque chose, car le type que je fournis en tant que deuxième paramètre ne fait aucune différence, quel que soit le premier paramètre. Alorsbind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))
devrait faire l'affaire aussi. (Bien que je préfère avoirbind
utilisable en tant que décorateur, personnellement, mais c'est une autre affaire.)__dict__
et l'accès aux attributs vous donne des méthodes non liées ou liées via le protocole de descripteur normal. J'ai toujours supposé que c'était une sorte de magie qui s'était produite pendanttype.__new__()
Cela peut être fait proprement avec types.MethodType . Exemple:
la source
__get__
). Je ne sais pas pour quelle version de python vous l'avez testé, mais sur python 3.4, laMethodType
fonction prend deux arguments. La fonction et l'instance. Cela devrait donc être changé entypes.MethodType(f, C())
.wgt.flush = types.MethodType(lambda self: None, wgt)
Créer une fermeture avec soi-même ne liera pas techniquement la fonction, mais c'est une manière alternative de résoudre le même problème sous-jacent (ou très similaire). Voici un exemple trivial:
la source
functools.partial(handler, self)
Cela se liera
self
àhandler
:Cela fonctionne en passant
self
comme premier argument à la fonction.object.function()
est juste du sucre syntaxique pourfunction(object)
.la source
functools.partial
au lieu de définir un lambda. Cela ne résout pas le problème exact, cependant. Vous avez toujours affaire à unfunction
au lieu d'uninstancemethod
.function
dont le premier argument a été partiel etinstancemethod
; la frappe de canard ne peut pas voir la différence.functools.partial
supprime certaines métadonnées, par exemple__module__
. (Aussi je veux dire pour l'enregistrement que je grince des dents quand je regarde mon premier commentaire sur cette réponse.) En fait, dans ma question, je mentionne que j'utilise déjàfunctools.partial
mais j'ai senti qu'il devait y avoir un moyen plus "pur", car il est facile d'obtenir à la fois des méthodes non liées et des méthodes liées.En retard à la fête, mais je suis venu ici avec une question similaire: j'ai une méthode de classe et une instance, et je veux appliquer l'instance à la méthode.
Au risque de trop simplifier la question de l'OP, j'ai fini par faire quelque chose de moins mystérieux qui pourrait être utile à d'autres qui arrivent ici (mise en garde: je travaille en Python 3 - YMMV).
Considérez cette classe simple:
Voici ce que vous pouvez en faire:
la source
meth
être invocable sans avoir à lui envoyer l'a
argument (c'est pourquoi j'ai initialement utiliséfunctools.partial
) - mais c'est préférable si vous n'avez pas besoin de passer la méthode et que vous pouvez simplement invoquez-le sur place. De plus, cela fonctionne de la même manière dans Python 2 que dans Python 3.