When you access a function through an instance that is defined on the class, a bound-method object is created each time. From the docs:
What exactly happens when a method is called? You may have noticed
that x.f()
was called without an argument above, even though the
function definition for f()
specified an argument. What happened to
the argument? Surely Python raises an exception when a function that
requires an argument is called without any — even if the argument
isn’t actually used…
Actually, you may have guessed the answer: the special thing about
methods is that the instance object is passed as the first argument of
the function. In our example, the call x.f()
is exactly equivalent to
MyClass.f(x)
. In general, calling a method with a list of n arguments
is equivalent to calling the corresponding function with an argument
list that is created by inserting the method’s instance object before
the first argument.
If you still don’t understand how methods work, a look at the
implementation can perhaps clarify matters. When an instance attribute
is referenced that isn’t a data attribute, its class is searched. If
the name denotes a valid class attribute that is a function object, a
method object is created by packing (pointers to) the instance object
and the function object just found together in an abstract object:
this is the method object.
Note, this happens every time you access a method:
>>> class Foo:
... def bar(self): pass
...
>>> f = Foo()
>>> f.bar is f.bar
False
How does this work? Well, actually, functions are descriptor objects, note the presence of a __get__
:
>>> def func(): pass
...
>>> func.__get__
<method-wrapper '__get__' of function object at 0x101e38ae8>
In other words, you can think of Python functions as being implemented thusly:
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)
Of course, they aren't implemented in Python (in CPython at least!).
Also note, accessing the function directly on the class, of course, doesn't do this (at least on Python 3, which you've tagged on your question):
>>> Foo.bar is Foo.bar
True