Python 绑定和未绑定方法对象的id()s——不同对象有时相同,同一对象有时不同

Python 绑定和未绑定方法对象的id()s——不同对象有时相同,同一对象有时不同,python,object,methods,Python,Object,Methods,我试过一些关于绑定和未绑定方法的代码。当我们调用它们时,我认为它们都会返回对象。但是当我使用id()获取一些信息时,它会返回一些我不理解的信息 IDE:Eclipse 插件:pydev Class C(object): def foo(self): pass cobj = C() print id(C.foo) #1 print id(cobj.foo) #2 a = C.foo b = cobj.foo print id(a) #3 prin

我试过一些关于绑定和未绑定方法的代码。当我们调用它们时,我认为它们都会返回对象。但是当我使用
id()
获取一些信息时,它会返回一些我不理解的信息

IDE:Eclipse

插件:pydev

Class C(object):
    def foo(self):
        pass

cobj = C()

print id(C.foo)    #1
print id(cobj.foo) #2

a = C.foo
b = cobj.foo

print id(a)        #3
print id(b)        #4
输出是

5671672

5671672

5671672

5669368
为什么#1和#2返回相同的id?它们不是不同的物体吗?如果我们将
C.foo
conj.foo
分配给两个变量,#3和#4返回不同的id

我认为#3和#4表明它们不是同一个对象,但是#1和#2


绑定方法的id和未绑定方法之间有什么区别?

每当您通过
实例.name
查找方法时(在Python 2中,
类.name
),都会创建一个新的方法对象。Python每次都使用将函数包装到方法对象中

因此,当您查找
id(C.foo)
时,将创建一个新的方法对象,您将检索其id(内存地址),然后再次丢弃该方法对象。然后查找
id(cobj.foo)
,这是一个新创建的方法对象,它重新使用了现在释放的内存地址,并且您看到了相同的值。然后,该方法再次被丢弃(当引用计数降至0时,垃圾被收集)

接下来,将对
C.foo
unbound方法的引用存储在变量中。现在没有释放内存地址(引用计数是1,而不是0),您可以通过查找
cobj.foo
创建第二个方法实例,该实例必须使用新的内存位置。因此得到两个不同的值

见:

返回对象的“标识”。这是一个整数(或长整数),保证该对象在其生存期内唯一且不变两个生命周期不重叠的对象可能具有相同的
id()

CPython实现细节:这是内存中对象的地址

我的

您可以通过类的
\uuu dict\uuu
属性使用对函数的直接引用重新创建方法,然后调用:

>>C类(对象):
...     def foo(self):
...         通过
... 
>>>C.foo
>>>C.uuuu dict_uuuuuuuuu['foo']
>>>C.uuu dict_uuuuu['foo']。uuuu get_uuuu(无,C)
>>>C.uuu dict_uuuu['foo'].uuuu get_uuu(C(),C)
注意,在Python3中,整个unbound/bound方法的区别已经被删除;您得到一个函数,在此之前您将得到一个未绑定的方法,否则将得到一个方法,其中一个方法始终是绑定的:

>>C.foo
>>>C.foo.\uuuuuuuuuuuuuuuu(无,C)
>>>C.foo.\uuuuu获取\uuuuu(C(),C)

此外,Python3.7添加了一个新的操作码对,它精确地替换了当前的
LOAD\u属性
-
CALL\u函数
操作码对,以避免每次都创建一个新的方法对象。此优化将
instance.foo()
的执行路径从
type(instance).\uu dict\uuuu['foo'].\uu get\uuuu(instance,type(instance))(
转换为
type(instance).\uu dict\uuuu['foo'](instance)
,因此将实例直接“手动”传递到函数对象。

添加到@Martijn Pieters的非常好:


@AshwiniChaudhary:这是CPython的一个实现细节;其他python实现可能对
id()
使用不同的值。从外观上看可能更好:
MethodType(vars(C)['foo',C(),C)
。但这样的代码并不常见。@eryksun:我的重点是首先说明python是如何创建方法的;显示它不是为一个类创建一次然后每次检索的。使用
types.MethodType()
将无法达到这个目的。@Vinny:因为Python是高度动态的
instance.method()
会导致从一个调用到下一个调用的函数对象完全不同。@Vinny:Python 3.7现在避免了在最常见的情况下创建方法对象,这要归功于;这适用于在同一表达式中有一个调用直接跟随的属性查找。我在这里的回答中已经提到了这一点。
>>> class C(object):
...     def foo(self):
...         pass
... 
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x1088cc488>
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x1088d6f90>>
>>> C.foo
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(None, C)
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x10bc65150>>
In [1]: class C(object):
   ...:     def foo(self):
   ...:         pass
   ...:

In [2]: c = C()

In [3]: id(c.foo), id(C.foo)
Out[3]: (149751844, 149751844)  # so 149751844 is current free memory address

In [4]: a = c.foo  # now 149751844 is assigned to a

In [5]: id(a)              
Out[5]: 149751844

# now python will allocate some different address to c.foo and C.foo     

In [6]: id(c.foo), id(C.foo)    # different address used this time, and
Out[6]: (149752284, 149752284)  # that address is freed after this step

# now 149752284 is again free, as it was not allocated to any variable

In [7]: b = C.foo  # now 149752284 is allocated to b    

In [8]: id(b)
Out[8]: 149752284                

In [9]: c.foo is C.foo  # better use `is` to compare objects, rather than id()
Out[9]: False