142

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 would 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 am 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?

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
Dan Passaro
  • 4,211
  • 2
  • 29
  • 33

6 Answers6

212

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
  • 25,754
  • 12
  • 83
  • 121
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 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
  • 12
    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
  • 4
    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
  • 2
    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
  • @MadPhysicist It’s okay but unnecessary to provide the second `owner` argument as it’s not used by functions’ `__get__` method. The only context I’m aware of where the `owner` parameter is used is in `classmethod`. See [this answer](https://stackoverflow.com/a/66767084/2326961) for more details. – Géry Ogam May 22 '23 at 03:20
94

This can be done with types.MethodType:

import types

bound_handler = types.MethodType(handler, self)
Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
Kiv
  • 31,940
  • 6
  • 44
  • 59
  • 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
  • 5
    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
12

With a closure, also known as a closed expression (as opposed to an open expression), which is an expression without free variables:

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

Here handler and self are free variables in the inner lambda expression and bound variables in the outer lambda expression, and args and kwargs are bound variables in both the inner and outer lambda expressions, so the outer lambda expression is a closure.

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
10

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. obj.f() is just syntactic sugar for f(obj).

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
brian-brazil
  • 31,678
  • 6
  • 93
  • 86
  • 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
  • 10
    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
0

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
  • 33,645
  • 23
  • 135
  • 217
  • 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
0

To expand on @Keith Pinson’s answer which uses a closure and @brian-brazil’s answer which does not, here is why the former is correct.

Example with a closure:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = (lambda handler, self:
    lambda *args, **kwargs: handler(self, *args, **kwargs)
)(handler, self)
bound_handler()  # returns True
self = False
bound_handler()  # returns True
handler = print
bound_handler()  # returns True

The outer lambda expression is closed so its bound variables handler and self cannot be rebound. __get__, types.MethodType, and functools.partial have the same behaviour.

Example without a closure:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs)
bound_handler()  # returns True
self = False
bound_handler()  # returns False
handler = print
bound_handler()  # prints False

The lambda expression is open so its free variables handler and self can be rebound.

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67