149

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?

1
  • 7
    @Christopher - A method that isn't bound to the scope of the object it was sucked from, so you have to pass self explicitly. Commented Jun 18, 2009 at 21:42

6 Answers 6

220

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 
Sign up to request clarification or add additional context in comments.

9 Comments

That's pretty cool. I like how you can omit the type and get back a "bound method ?.f" instead.
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.
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')
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.)
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__()
|
105

This can be done with types.MethodType:

import types bound_handler = types.MethodType(handler, self) 

4 Comments

+1 This is awesome, but there's no reference to it in the python docs at the URL you provided.
+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()).
Here it is! It's a good way to patch instance methods: wgt.flush = types.MethodType(lambda self: None, wgt)
It is actually mentioned in the docs, but in the descriptor page from the other answer: docs.python.org/3/howto/descriptor.html#functions-and-methods
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.

1 Comment

Yes, this is about the same as my original fix, which was to use functools.partial(handler, self)
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).

5 Comments

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
No it doesn't, it'll only call the method if you do bound_handler(). Defining a lambda does not call the lambda.
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: what's the difference between a function whose first argument you partial-ed and instancemethod; duck typing can't see the difference.
@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.
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 

2 Comments

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.
Apologies for not reading your original requirements more carefully. I am partial (pun intended) to the lambda-based approach given by @brian-brazil in stackoverflow.com/a/1015355/558639 -- it's about as pure as you can get.
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.