这是一个非常有趣的问题!

在你的条件下,它们看起来是一样的: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)

但是,请注意,一旦对它们执行任何操作,它们就会变得不同:

^{pr2}$

然后,当再次测试时,它们又变了!

^{3}$

当访问实例上的方法时,返回“绑定方法”的实例。绑定方法存储对实例和方法的函数对象的引用:>>> a_m

>
>>> a_m.im_func is Foo.__dict__['method']
True
>>> a_m.im_self is a
True

(请注意,我需要使用Foo.__dict__['method'],而不是Foo.method,因为Foo.method将产生一个“未绑定方法”……其目的留给读者作为练习)

这个“绑定方法”对象的目的是使方法在像函数一样传递时“行为合理”。例如,当我调用函数a_m()时,这与调用a.method()是完全相同的,即使我们不再明确引用a。将这种行为与JavaScript进行对比(例如,var method = foo.method; method()不会产生与foo.method()相同的结果。

所以!这让我们回到最初的问题:为什么id(a.method)产生的值与id(b.method)相同?我相信Asad是正确的:它与Python的引用计数垃圾收集器有关:当表达式id(a.method)被求值时,会分配一个绑定方法,计算ID,并释放绑定方法。当分配下一个绑定方法(for b.method)时,它将被分配到内存中完全相同的位置,因为自从分配a.method的绑定方法以来,没有任何(或平衡数量的)分配。这意味着a.method似乎与b.method具有相同的内存位置。

最后,这解释了为什么内存位置在第二次检查时会发生变化:在第一次和第二次检查之间发生的其他分配意味着,第二次,它们被分配到不同的位置(注意:它们被重新分配是因为对它们的所有引用都丢失了;绑定方法被缓存†,因此访问同一方法两次将返回相同的实例:a_m0 = a.method; a_m1 = a.method; a_m0 is a_m1 => True)。

*:pedants注意:实际上,这与实际的垃圾收集器没有任何关系,它只存在于处理循环引用……但是……这是另一天的故事。

†:至少在CPython 2.7中;CPython 2.6似乎没有缓存绑定的方法,这会导致我认为没有指定行为。