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())
Class method differences in Python: bound, unbound and static
In Python, there is a distinction between bound and unbound methods.
Basically, a call to a member function (like method_one
), a bound function
a_test.method_one()
is translated to
Test.method_one(a_test)
i.e. a call to an unbound method. Because of that, a call to your version of method_two
will fail with a TypeError
>>> a_test = Test()
>>> a_test.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)
You can change the behavior of a method using a decorator
class Test(object):
def method_one(self):
print "Called method_one"
@staticmethod
def method_two():
print "Called method two"
The decorator tells the built-in default metaclass type
(the class of a class, cf. this question) to not create bound methods for method_two
.
Now, you can invoke static method both on an instance or on the class directly:
>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two
What's the difference between a 'function', 'method' and 'bound method' in Python 3?
Is 'method' a type equivalent to 'unbound method' in Python 2?
Kind-a-sort-a. But not really. It is a method_descriptor
object defined in C code. It is an unbound method, but not the kind you found in Python 2.
For Python types written C, all 'methods' are really C functions. The <method 'name' of 'type' objects>
object you found is a special object you can use to call that function given an instance and further arguments, just like the function
object does for custom Python classes. The object is defined in C in the PyMethodDescr_Type
structure. It implements the descriptor protocol, just like functions do.
Python defines several other such descriptor types; if you use __slots__
, each attribute is a dsescriptor of type member_descriptor
(see the PyMemberDescr_Type
structure), while classmethod
, property
and staticmethod
are perhaps better known descriptor objects.
In Python 2, bound and unbound methods are really just one type, instancemethod
(defined by the PyMethod_Type
structure); it'll report as bound if the __self__
(im_self
) attribute is set. In Python 3 using a function as a descriptor simply doesn't produce method objects without __self__
set; instead calling function.__get__()
with no instance just returns the function again.
The only reason Python 2 returns unbound methods is to enforce a type check; the first argument must be an instance of the class (or a subclass thereof). This didn't make all that much sense for Python code that supports duck-typing, so in Python 3 the restriction was removed. However, with C code you can't use duck-typing, you still have to restrict the type, and that's why C-types still return a method_descriptor
object that enforces this restriction.
Differences between methods of a class, which are function and which are bound method ?
This answer will be really technical, I hope it's still understandable though. The problem is that it requires knowledge of the descriptor protocol to understand how methods in Python work.
All functions in Python 3 are descriptors, to be precise they are non-data descriptors. That means they implements a __get__
method - but no __set__
method.
That's interesting because descriptors can do (almost) anything if they are looked up on a class or an instance.
By the definition of the __get__
method in Pythons data model:
object.__get__(self, instance, owner)
Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access).
owner
is always the owner class, whileinstance
is the instance that the attribute was accessed through, orNone
when the attribute is accessed through theowner
. This method should return the (computed) attribute value or raise anAttributeError
exception.
So what does this have to do with the difference between function
and bound_method
?
It's easy, a function accessed through __get__
with an instance=None
will return itself:
>>> def func(x): return x
>>> func.__get__(None, object)
<function __main__.func>
>>> func.__get__(None, object) is func
True
While it will be a bound_method
if accessed with an not-None instance:
>>> func.__get__(object())
<bound method func of <object object at 0x00000155614A0610>>
It's basically just a wrapper around func
with the instance stored:
>>> m = func.__get__(object())
>>> m.__self__ # stored instance
<object at 0x155614a0650>
>>> m.__func__ # stored function
<function __main__.func>
However, when called, it will pass the instance as first argument to the wrapped function:
>>> m()
<object at 0x155614a0650>
So, bound method
s will pass the instance as first argument, while function
s do not (they requires all attributes).
So when you look at a class all normal methods will display as functions while all normal methods on an instance will be bound methods
.
Why did I mention normal methods? Because you can define arbitrary descriptors. For example the Python built-ins already contain several exceptions:
classmethod
staticmethod
property
(this is in fact a data-descriptor so I'll neglect it in the following discussion)
classmethod
s will display as bound method
even when looked up on the class. That's because they are meant to be callable on the class and pass the class to the function, no matter if they are called on the class or the instance:
class Test(object):
@classmethod
def func(x):
return x
>>> Test.func
<bound method Test.func of <class '__main__.Test'>>
>>> Test().func
<bound method Test.func of <class '__main__.Test'>>
And staticmethod
s always display as functions because they never pass anything additional to the function:
class Test(object):
@staticmethod
def func(x):
return x
>>> Test().func
<function __main__.Test.func>
>>> Test.func
<function __main__.Test.func>
So it's easily possible to see also bound method
s on the class (e.g. classmethod
s) and likewise one could also find normal function
s on instances (e.g. staticmethod
s).
is it necessary to distinguish method and function in python3?
As a matter of terminology, methods are attached to class instances (or classes themselves, for classmethod
s and staticmethod
s), functions aren't.
In practice, in Python 3, the distinction is much weaker than it used to be. Originally, in Python 2, "methodness" was more important; defining a function within a class made it an unbound method if referenced from the class, and a bound method if referenced on the instances.
In Python 3, the concept of unbound methods is gone; when referenced from the class itself, you get a plain function, not a method (bound or unbound); you only get a bound method when you reference the method from an instance of the class.
Essentially, there are really two categories of things now:
- Functions which can't be bound as methods (because they don't implement the descriptor protocol to produce bound methods; applies solely to built-in functions in CPython)
- Functions which can be bound as methods (includes all functions defined in Python itself)
Everything in category 2 is a function which can act as a method if attached to a class, then referenced from an instance of said class. The nomenclature describes the intent, but as a matter of implementation, there is very little difference left.
Note that even in Python 2, the two category approach was the same, it's just that the descriptor protocol for functions was invoked even when loaded from the class itself (to bypass it and get the raw function without creating an unbound method, you had to do ClassName.__dict__['methodname']
). So it's always been the case that methods are just the result of binding functions as a matter of implementation.
id()s of bound and unbound method objects --- sometimes the same for different objects, sometimes different for the same object
Whenever you look up a method via instance.name
(and in Python 2, class.name
), the method object is created a-new. Python uses the descriptor protocol to wrap the function in a method object each time.
So, when you look up id(C.foo)
, a new method object is created, you retrieve its id (a memory address), then discard the method object again. Then you look up id(cobj.foo)
, a new method object created that re-uses the now freed memory address and you see the same value. The method is then, again, discarded (garbage collected as the reference count drops to 0).
Next, you stored a reference to the C.foo
unbound method in a variable. Now the memory address is not freed (the reference count is 1, instead of 0), and you create a second method instance by looking up cobj.foo
which has to use a new memory location. Thus you get two different values.
See the documentation for id()
:
Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same
id()
value.CPython implementation detail: This is the address of the object in memory.
Emphasis mine.
You can re-create a method using a direct reference to the function via the __dict__
attribute of the class, then calling the __get__
descriptor method:
>>> class C(object):
... def foo(self):
... pass
...
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x1088cc488>
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x1088d6f90>>
Note that in Python 3, the whole unbound / bound method distinction has been dropped; you get a function where before you'd get an unbound method, and a method otherwise, where a method is always bound:
>>> C.foo
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(None, C)
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x10bc65150>>
Furthermore, Python 3.7 adds a new LOAD_METHOD
- CALL_METHOD
opcode pair that replaces the current LOAD_ATTRIBUTE
- CALL_FUNCTION
opcode pair precisely to avoid creating a new method object each time. This optimisation transforms the executon path for instance.foo()
from type(instance).__dict__['foo'].__get__(instance, type(instance))()
with type(instance).__dict__['foo'](instance)
, so 'manually' passing in the instance directly to the function object.
Python: Bind an Unbound Method?
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
Related Topics
How to Get Attribute of Element from Selenium
Should I Always Specify an Exception Type in 'Except' Statements
Stop Reading Process Output in Python Without Hang
What Is the Most Efficient Way of Finding All the Factors of a Number in Python
How to Check If One of the Following Items Is in a List
Comparing Two Dictionaries and Checking How Many (Key, Value) Pairs Are Equal
What's the Bad Magic Number Error
How to Selectively Escape Percent (%) in Python Strings
How to Include Package Data with Setuptools/Distutils
Keras Dense Layer's Input Is Not Flattened
How to Generate Keyboard Events
How to Use Python to Login to a Webpage and Retrieve Cookies for Later Usage