Reputation: 99408
I have experimented a little. By checking __dict__
of a class or an instance, I can see some method has type function
and some bound method
. The experiment is messy, and I can't figure the following questions out.
In Python 3, what are the differences between methods of a class or instance, which are "function" and which are "bound method"?
How are they created respectively?
Can they both be called on a class and on an instance? Will they both be implicitly given an instance as their first argument?
Is "bound method" an attribute of a class or an instance of a class?
Thanks.
Upvotes: 2
Views: 211
Reputation: 152607
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).
Upvotes: 6