Python 为什么同一类的不同对象的方法具有相同的id?

Python 为什么同一类的不同对象的方法具有相同的id?,python,object,methods,Python,Object,Methods,在下面的代码中,我不明白为什么无用的\u func属于两个不同的对象时具有相同的id class parent(object): @classmethod def a_class_method(cls): print "in class method %s" % cls @staticmethod def a_static_method(): print "static method" def useless_func(self):

在下面的代码中,我不明白为什么
无用的\u func
属于两个不同的对象时具有相同的id

class parent(object):
   @classmethod
   def a_class_method(cls):
     print "in class method %s" % cls

   @staticmethod
   def a_static_method():
     print "static method"

   def useless_func(self):
     pass

 p1, p2 = parent(),parent()

 id(p1) == id(p2) // False

 id(p1.useless_func) == id(p2.useless_func) // True

这是一个非常有趣的问题

在您的条件下,它们看起来是一样的:

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
>>a\m
>>>a_m.im_func是Foo.uuu dict_uuuu['method']
真的
>>>自我就是自我
真的
(请注意,我需要使用
Foo.\uu dict\uu['method']
,而不是
Foo.method
,因为
Foo.method
将生成一个“未绑定的方法”…其目的留给读者作为练习)

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

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

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

*:学究注:实际上,这与实际的垃圾收集器无关,垃圾收集器的存在只是为了处理循环引用……但是……这是另一天的故事。

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

以下是我认为正在发生的情况:

  • 取消引用
    p1.func
    时,会在内存中创建其副本。此内存位置由
    id
  • 因为没有对刚刚创建的方法副本的引用,所以GC会回收它,并且内存地址再次可用
  • 当您取消引用p2.func时,会在相同的内存地址(它可用)中创建它的副本,您可以再次使用
    id
    检索它
  • 第二份是GCd
  • 如果要运行一组其他代码并再次检查实例方法的id,我敢打赌
    id
    s将彼此相同,但与最初的运行不同

    此外,您可能会注意到,在David Wolver的示例中,只要获得了对方法副本的持久引用,
    id
    s就会变得不同

    为了证实这一理论,下面是一个使用Jython的shell会话(与PyPy的结果相同),它不使用CPython的引用计数垃圾收集:

    Jython 2.5.2 (Debian:hg/91332231a448, Jun 3 2012, 09:02:34) 
    [OpenJDK Server VM (Oracle Corporation)] on java1.7.0_21
    Type "help", "copyright", "credits" or "license" for more information.
    >>> class parent(object):
    ...     def m(self):
    ...             pass
    ... 
    >>> p1, p2 = parent(), parent()
    >>> id(p1.m) == id(p2.m)
    False
    

    可能重复@AnkitJaiswal它是不同的我相信在你的第二个例子中,你有两个不同的引用,但是a.method和b.method仍然有相同的id。@Hamish id不是这样工作的:
    a=[]b=a id(a)==id(b)True
    lst=[1,2,3];len({id(lst2.append)表示范围(1000000)})
    对于不同的运行(通常在1-3之间)实际会产生不同的结果…应该查看源代码,看看它是如何准确处理的…实际上,我只是在IPython控制台上运行时似乎不一致…(我认为这是在幕后进行其他调用?)@DavidWolever绑定的方法是缓存的,因此访问同一方法两次将返回同一实例
    a_m0=a.method;a_m1=a.method;a_m0是a_m1=>True
    。这不一定是真的。我在2.6.5上(r265:79063,2010年4月16日,13:57:41)[GCC 4.4.3].啊,我们有一个赢家!这很有道理。回答很好。@jamylak这是一个非常慷慨的编辑。事实上,我相信那里有足够的内容来保证一个单独的答案。@Asad我不相信,它属于这里