这是一个非常有趣的问题!
在您的条件下,它们看起来确实相同:
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)
但是,请注意,一旦您对它们进行任何操作,它们就会变得不同:
>>> a_m = a.method
>>> b_m = b.method
>>> id(a_m), id(b_m)
(4547151*9*04, 4547151*5*04)
然后,当再次测试时,它们又变了!
>>> id(b.method)
4547304416
>>> id(a.method)
4547304416
当访问实例上的方法时,将返回“绑定方法”的实例。绑定方法存储对实例和方法的函数对象的引用:
>>> 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
(请注意,我需要使用Foo.__dict__['method']
, 而不是Foo.method
,因为Foo.method
会产生一个“未绑定的方法”……其目的留给读者作为练习)
这个“绑定方法”对象的目的是使方法在像函数一样被传递时“表现得明智”。例如,当我调用 function 时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,并释放绑定方法。当下一个绑定方法 forb.method
被分配时,它被分配到内存中完全相同的位置,因为自从分配了绑定方法 for 以来,没有任何(或数量均衡的)a.method
分配。这意味着a.method
似乎与 具有相同的内存位置b.method
。
最后,这解释了为什么内存位置在第二次检查时似乎发生了变化:在第一次和第二次检查之间发生的其他分配意味着,第二次,它们被分配在不同的位置(注意:它们被重新分配,因为对它们的所有引用都丢失了;绑定的方法被缓存†,因此两次访问相同的方法将返回相同的实例:) a_m0 = a.method; a_m1 = a.method; a_m0 is a_m1 => True
。
*:学究们注意:实际上,这与实际的垃圾收集器无关,它只是为了处理循环引用而存在......但是......这是另一天的故事。
†:至少在 CPython 2.7 中;CPython 2.6 似乎没有缓存绑定方法,这让我认为没有指定行为。