What is the difference between a function, an unbound method and a bound method?
Solution 1
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())
Solution 2
is quite hard to understand
Well, it is quite a hard topic, and it has to do with descriptors.
Lets start with function. Everything is clear here - you just call it, all supplied arguments are passed while executing it:
>>> f = A.__dict__['f1']
>>> f(1)
1
Regular TypeError
is raised in case of any problem with number of parameters:
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)
Now, methods. Methods are functions with a bit of spices. Descriptors come in game here. As described in Data Model, A.f1
and A().f1
are translated into A.__dict__['f1'].__get__(None, A)
and type(a).__dict__['f1'].__get__(a, type(a))
respectively. And results of these __get__
's differ from the raw f1
function. These objects are wrappers around the original f1
and contain some additional logic.
In case of unbound method
this logic includes a check whether first argument is an instance of A
:
>>> f = A.f1
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)
If this check succeeds, it executes original f1
with that instance as first argument:
>>> f(A())
<__main__.A object at 0x800f238d0>
Note, that im_self
attribute is None
:
>>> f.im_self is None
True
In case of bound method
this logic immediately supplies original f1
with an instance of A
it was created of (this instance is actually stored in im_self
attribute):
>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>
So, bound
mean that underlying function is bound to some instance. unbound
mean that it is still bound, but only to a class.
Solution 3
A function object is a callable object created by a function definition. Both bound and unbound methods are callable objects created by a Descriptor called by the dot binary operator.
Bound and unbound method objects have 3 main properties: im_func
is the function object defined in the class, im_class
is the class, and im_self
is the class instance. For unbound methods, im_self
is None
.
When a bound method is called, it calls im_func
with im_self
as the first parameter followed by its calling parameters. unbound methods call the underlying function with just its calling parameters.
Starting with Python 3, there are no unbound methods. Class.method
returns a direct reference to the method.
Solution 4
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.
Solution 5
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)
- If a function is called without class or instance, it is a plain function.
-
If a function is called from a class or an instance, its
__get__
is called to retrieve wrapped function:
a.B.x
is same asB.__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 astype(b).__dict__['x'].__get__(b, type(b)
. This will return a bound method in both Python 2 and Python 3, which meansself
will be implicitly passed as first argument.
Benjamin Hodgson
I work here! It takes a lot of self-restraint for me not to abuse my live database privileges to artificially inflate my reputation score. [email protected]
Updated on July 07, 2022Comments
-
Benjamin Hodgson almost 2 years
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.
-
John La Rooy almost 12 yearsso "bound" means a method is "bound to an instance", "unbound" means it isn't
-
musiphil almost 9 yearsThis is where the real answer should be revealed: The main difference between a function and an unbound method is in the latter's support for the descriptor protocol; calling an unbound method requires an instance. But what does this mean? A function doesn't support the descriptor protocol? It does, as
f1.__get__
also works. -
ecatmur almost 9 years@musiphil yes, that was wrong. The real difference between a function and an unbound method is that the latter knows which class it is bound to; calling an unbound method requires an instance of its class type.
-
Benjamin Hodgson about 8 yearsThis answer could be improved with a comparison of Python 2 and Python 3's models of bound/unbound methods.
-
ecatmur about 8 years@BenjaminHodgson done, cheers. Perhaps it could do with its own question.