This is a very interesting question!
In your conditions, they look the same:
Python 2.7.2 (default, Oct 11 2012, 20:14:37) [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> class Foo(object): ... def method(self): pass ... >>> a, b = Foo(), Foo() >>> a.method == b.method False >>> id(a.method), id(b.method) (4547151904, 4547151904)
However, note that as soon as you do something with them, they become different:
>>> a_m = a.method >>> b_m = b.method >>> id(a_m), id(b_m) (4547151*9*04, 4547151*5*04)
And then, having tested again, they changed again!
>>> id(b.method) 4547304416 >>> id(a.method) 4547304416
When accessing an instance method, an instance of the "associated method" is returned. The associated method stores a reference to both the instance and the object of the method function:
>>> a_m <bound method Foo.method of <__main__.Foo object at 0x10f0e9a90>> >>> a_m.im_func is Foo.__dict__['method'] True >>> a_m.im_self is a True
(note that I need to use Foo.__dict__['method'] , not Foo.method , because Foo.method will give a "unrelated method" ... whose purpose remains as an exercise for the reader)
The purpose of this “related method” object is to make the methods “behave rationally” when they are passed as similar functions. For example, when I call the function a_m() , which is identical to calling a.method() , although we no longer have an explicit reference to a . Compare this behavior with JavaScript (for example), where var method = foo.method; method() var method = foo.method; method() does not give the same result as foo.method() .
SO! This brings us back to the first question: why does id(a.method) give the same meaning as id(b.method) ? I believe that Asad is right: this is related to counting the Python * garbage collector: when the id(a.method) expression id(a.method) , the associated method is distributed, the identifier is calculated, and the binding method is freed. When the next associated method is allocated - for b.method -, it is allocated exactly in the same place in memory, since there were no (or there was a balanced number) allocations, since the associated method for a.method was allocated. This means that a.method seems to have the same memory location as b.method .
Finally, this explains why the memory cells seem to change the second time they are checked: other allocations that occurred between the first and second checks mean that the second time they are allocated elsewhere (note: they are redistributed, because all references to them were lost, related methods are cached †, therefore, access to the same method returns the same instance twice: a_m0 = a.method; a_m1 = a.method; a_m0 is a_m1 => True ).
*: note of pedants: in fact, this has nothing to do with the actual garbage collector, which exists only for working with circular links ... but ... this is a story the other day. †: at least in CPython 2.7; CPython 2.6 does not seem to cache related methods, which would lead me to expect that the behavior is not specified.