Can someone please explain why it is so?
class Foo:
def bar(self):
pass
a = Foo()
b = Foo()
a.bar == b.bar # False
a.bar is b.bar # False
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 forf()
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 toMyClass.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