How to bind an unbound method without calling it?

Question:

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?

Asked By: Dan Passaro

||

Answers:

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).

Answered By: brian-brazil

This can be done with types.MethodType:

import types

bound_handler = types.MethodType(handler, self)
Answered By: Kiv

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
Answered By: Alex Martelli

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 self:
    lambda *args, **kwargs: handler(self, *args, **kwargs)
)(self)

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

Answered By: Keith Pinson

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
Answered By: fearless_fool

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

Example with a closure (__get__, types.MethodType, and functools.partial have the same behavior):

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


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

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
Answered By: Géry Ogam
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.