web-dev-qa-db-fra.com

Python: lier une méthode non liée?

En Python, existe-t-il un moyen de lier une méthode non liée 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 quelqu'un sait-il s'il existe une méthode Pythonic propre et saine pour lier une méthode non liée à une instance et continuer à la transmettre sans l'appeler?

107
Dan Passaro

Toutes les fonctions sont également des descripteurs , vous pouvez donc les lier en appelant leur __get__ méthode:

bound_handler = handler.__get__(self, MyWidget)

Voici l'excellent R. Hettinger guide aux descripteurs.


Comme un exemple autonome tiré de Keith'scomment :

def bind(instance, func, as_name=None):
    """
    Bind the function *func* to *instance*, with either provided name *as_name*
    or the existing name of *func*. The provided *func* should accept the 
    instance as the first argument, i.e. "self".
    """
    if as_name is None:
        as_name = func.__name__
    bound_method = func.__get__(instance, instance.__class__)
    setattr(instance, as_name, bound_method)
    return bound_method

class Thing:
    def __init__(self, val):
        self.val = val

something = Thing(21)

def double(self):
    return 2 * self.val

bind(something, double)
something.double()  # returns 42
159
Alex Martelli

Cela peut être fait proprement avec types.MethodType . Exemple:

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>>
77
Kiv

Créer une fermeture avec soi 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:

self.method = (lambda self: lambda args: self.do(args))(self)
9
Keith Pinson

Cela liera self à handler:

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

Cela fonctionne en passant self comme premier argument à la fonction. object.function() n'est que du sucre syntaxique pour function(object).

7
brian-brazil

Tard dans la soirée, 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 simplifier à outrance la question du PO, j'ai fini par faire quelque chose de moins mystérieux qui pourrait être utile à ceux qui arrivent ici (mise en garde: je travaille dans Python 3 - YMMV).

Considérez cette classe simple:

class Foo(object):

    def __init__(self, value):
        self._value = value

    def value(self):
        return self._value

    def set_value(self, value):
        self._value = value

Voici ce que vous pouvez en faire:

>>> meth = Foo.set_value   # the method
>>> a = Foo(12)            # a is an instance with value 12
>>> meth(a, 33)            # apply instance and method
>>> a.value()              # voila - the method was called
33
1
fearless_fool