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

Viewed 35194

I'm asking this question because of a discussion on the comment thread of this answer. I'm 90% of the way to getting my head round it.

In [1]: class A(object):  # class named 'A'
   ...:     def f1(self): pass
   ...:
In [2]: a = A()  # an instance

f1 exists in three different forms:

In [3]: a.f1  # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1  # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1']  # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1']  # a function
Out[6]: <function __main__.f1>

What is the difference between the bound method, unbound method and function objects, all of which are described by f1? How does one call these three objects? How can they be transformed into each other? The documentation on this stuff is quite hard to understand.

6 Answers

Please refer to the Python 2 and Python 3 documentation for more details.

My interpretation is the following.

Class Function snippets:

Python 3:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)

Python 2:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype)
  1. If a function is called without class or instance, it is a plain function.
  2. If a function is called from a class or an instance, its __get__ is called to retrieve wrapped function:
    a. B.x is same as B.__dict__['x'].__get__(None, B). In Python 3, this returns plain function. In Python 2, this returns an unbound function.

    b. b.x is same as type(b).__dict__['x'].__get__(b, type(b). This will return a bound method in both Python 2 and Python 3, which means self will be implicitly passed as first argument.

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

From the ground breaking what is a function perspective there is no difference. Python object oriented features are built upon a function based environment.

Being bound is equal to:

Will the function take the class (cls) or the object instance (self) as the first parameter or no?

Here is the example:

class C:

    #instance method 
    def m1(self, x):
        print(f"Excellent m1 self {self} {x}")

    @classmethod
    def m2(cls, x):
        print(f"Excellent m2 cls {cls} {x}")

    @staticmethod
    def m3(x):
        print(f"Excellent m3 static {x}")    

ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)

print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)

Outputs:

Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>

The output shows the static function m3 will be never called bound. C.m2 is bound to the C class because we sent the cls parameter which is the class pointer.

ci.m1 and ci.m2 are both bound; ci.m1 because we sent self which is a pointer to the instance, and ci.m2 because the instance knows that the class is bound ;).

To conclude you can bound method to a class or to a class object, based on the first parameter the method takes. If method is not bound it can be called unbound.


Note that method may not be originally part of the class. Check this answer from Alex Martelli for more details.

Related