129

In Python, is there a way to bind an unbound method without calling it?

I am writing a wxPython program, and for a certain class I decided it'd be nice to group the data of all of my buttons together as a class-level list of tuples, like 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)

The problem is, since all of the values of handler are unbound methods, my program explodes in a spectacular blaze and I weep.

I was looking around online for a solution to what seems like should be a relatively straightforward, solvable problem. Unfortunately I couldn't find anything. Right now, I'm using functools.partial to work around this, but does anyone know if there's a clean-feeling, healthy, Pythonic way to bind an unbound method to an instance and continue passing it around without calling it?

Dan Passaro
  • 3,741
  • 2
  • 22
  • 31

5 Answers5

192

All functions are also descriptors, so you can bind them by calling their __get__ method:

bound_handler = handler.__get__(self, MyWidget)

Here's R. Hettinger's excellent guide to descriptors.


As a self-contained example pulled from Keith's comment:

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
Nick T
  • 22,202
  • 10
  • 72
  • 110
Alex Martelli
  • 762,786
  • 156
  • 1,160
  • 1,345
  • 3
    That's pretty cool. I like how you can omit the type and get back a "bound method ?.f" instead. – Kiv Jun 18 '09 at 22:02
  • I like this solution over the `MethodType` one, because it works the same in py3k, while `MethodType`'s arguments have been changed up a bit. – bgw Mar 08 '11 at 01:27
  • 10
    And thus, a function to bind functions to class instances: `bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))` Example: `class A: pass;` `a = A();` `bind(a, bind, 'bind')` – Keith Pinson Aug 13 '11 at 16:53
  • 2
    Huh, you learn something new every day. @Kazark In Python 3, at least, you can also skip supplying the type, as `__get__` will take that implicitly from the object parameter. I'm not even sure if supplying it does anything, as it makes no difference what type I supply as the second parameter regardless of what the first parameter is an instance of. So `bind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))` should do the trick as well. (Though I'd prefer having `bind` usable as a decorator, personally, but that's a different matter.) – JAB Jan 28 '14 at 21:01
  • I just learned that functions are also descriptors for the first time thanks to @AlexMartelli – minmaxavg Dec 25 '15 at 05:09
  • You sir are awesome. I have been looking for a way to fix this for the last couple of weeks. Finally came up with the right search terms and you solved my anguish in one brief line and a link to some great docs. I can now configure my logging to a ridiculous extent, not just by adding new level methods to the Logger class, but specific methods for specific instances. – Mad Physicist Sep 23 '16 at 20:15
  • Just to clarify, is it OK to do `handler.__get__(self, type(self))`? Is there ever a context when you would not want to make the second parameter `type(self)`? – Mad Physicist Jan 27 '17 at 18:23
  • 1
    Wow, never knew functions were descriptors. That's a very elegant design, methods are just plain functions in the class' `__dict__` and attribute access gives you unbound or bound methods through the normal descriptor protocol. I always assumed it was some sort of magic that happened during `type.__new__()` – JaredL Dec 09 '17 at 20:08
87

This can be done cleanly with types.MethodType. Example:

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>>
Kiv
  • 28,631
  • 4
  • 41
  • 57
  • 10
    +1 This is awesome, but there's no reference to it in the python docs at the URL you provided. – Kyle Wild Dec 23 '11 at 13:30
  • 12
    +1, I prefer not to have calls to magic functions in my code (i.e. `__get__`). I don't know for which version of python this you tested this on, but on python 3.4, the `MethodType` function takes two arguments. The function and the instance. So this should be changed to `types.MethodType(f, C())`. – Dan Milon Oct 08 '14 at 12:48
  • 1
    Here it is! It's a good way to patch instance methods: `wgt.flush = types.MethodType(lambda self: None, wgt)` – Winand Jul 31 '15 at 07:35
  • 3
    It is actually mentioned in the docs, but in the descriptor page from the other answer: https://docs.python.org/3/howto/descriptor.html#functions-and-methods – kai Aug 08 '18 at 11:27
10

Creating a closure with self in it will not technically bind the function, but it is an alternative way of solving the same (or very similar) underlying problem. Here's a trivial example:

self.method = (lambda self: lambda args: self.do(args))(self)
Keith Pinson
  • 7,162
  • 5
  • 54
  • 97
9

This will bind self to handler:

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

This works by passing self as the first argument to the function. object.function() is just syntactic sugar for function(object).

brian-brazil
  • 24,975
  • 4
  • 64
  • 67
  • 1
    Yes, but this calls the method. The problem is I need to be able to pass the bound method as a callable object. I have the unbound method and the instance I'd like it to be bound to, but can't figure out how to put it all together without immediately calling it – Dan Passaro Jun 18 '09 at 21:54
  • 9
    No it doesn't, it'll only call the method if you do bound_handler(). Defining a lambda does not call the lambda. – brian-brazil Jun 18 '09 at 21:55
  • 1
    You could actually use `functools.partial` instead of defining a lambda. It doesn't solve the exact problem, though. You're still dealing with a `function` instead of an `instancemethod`. – Alan Plum Feb 23 '11 at 20:20
  • 5
    @Alan: what's the difference between a `function` whose first argument you partial-ed and `instancemethod`; duck typing can't see the difference. – Lie Ryan Mar 06 '11 at 14:15
  • 2
    @LieRyan the difference is that you're still not dealing with the fundamental type. `functools.partial` drops some metadata, e.g. `__module__`. (Also I wanna state for the record I cringe real hard when I look at my first comment on this answer.) In fact in my question I mention I'm already using `functools.partial` but I felt like there had to be a "purer" way, since it's easy to get both unbound and bound methods. – Dan Passaro Oct 06 '15 at 00:28
1

Late to the party, but I came here with a similar question: I have a class method and an instance, and want to apply the instance to the method.

At the risk of oversimplifying the OP's question, I ended up doing something less mysterious that may be useful to others who arrive here (caveat: I'm working in Python 3 -- YMMV).

Consider this simple class:

class Foo(object):

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

    def value(self):
        return self._value

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

Here's what you can do with it:

>>> 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
fearless_fool
  • 29,889
  • 20
  • 114
  • 193
  • This doesn't solve my issue - which is that I wanted `meth` to be invokable without having to send it the `a` argument (which is why I initially used `functools.partial`) - but this is preferable if you don't need to pass the method around and can just invoke it on the spot. Also this works the same way in Python 2 as it does in Python 3. – Dan Passaro Jul 22 '18 at 17:27
  • Apologies for not reading your original requirements more carefully. I am partial (pun intended) to the lambda-based approach given by @brian-brazil in https://stackoverflow.com/a/1015355/558639 -- it's about as pure as you can get. – fearless_fool Jul 22 '18 at 22:48