What is the difference between a function, an unbound method and a bound method?

A function is created by the def statement, or by lambda. Under Python 2, when a function appears within the body of a class statement (or is passed to a type class construction call), it is transformed into an unbound method. (Python 3 doesn’t have unbound methods; see below.) When a function is accessed on a class instance, it is transformed into a bound method, that automatically supplies the instance to the method as the first self parameter.

def f1(self):
    pass

Here f1 is a function.

class C(object):
    f1 = f1

Now C.f1 is an unbound method.

>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True

We can also use the type class constructor:

>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>

We can convert f1 to an unbound method manually:

>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>

Unbound methods are bound by access on a class instance:

>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>

Access is translated into calling through the descriptor protocol:

>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

Combining these:

>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>

Or directly:

>>> types.MethodType(f1, C(), C)                
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

The main difference between a function and an unbound method is that the latter knows which class it is bound to; calling or binding an unbound method requires an instance of its class type:

>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>

Since the difference between a function and an unbound method is pretty minimal, Python 3 gets rid of the distinction; under Python 3 accessing a function on a class instance just gives you the function itself:

>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True

In both Python 2 and Python 3, then, these three are equivalent:

f1(C())
C.f1(C())
C().f1()

Binding a function to an instance has the effect of fixing its first parameter (conventionally called self) to the instance. Thus the bound method C().f1 is equivalent to either of:

(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())

Leave a Comment