Mik*_*ung 29 python methods object
我已经尝试了一些关于绑定和未绑定方法的代码.当我们调用它们时,我认为它们都会返回对象.但是当我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
Run Code Online (Sandbox Code Playgroud)
输出是......
5671672
5671672
5671672
5669368
Run Code Online (Sandbox Code Playgroud)
为什么#1和#2返回相同的ID?它们不是不同的对象吗?如果我们分配C.foo和conj.foo两个变量,#3和#4返回不同的id.
我认为#3和#4表明它们不是同一个对象,但#1和#2 ......
绑定方法的id和未绑定方法有什么区别?
Mar*_*ers 46
无论何时通过instance.name(以及在Python 2中class.name)查找方法,方法对象都是以新的方式创建的.Python使用描述符协议每次将函数包装在方法对象中.
因此,当您查找时id(C.foo),会创建一个新方法对象,检索其id(内存地址),然后再次丢弃方法对象.然后你查找id(cobj.foo),创建一个新的方法对象,重新使用现在释放的内存地址,你会看到相同的值.然后,再次丢弃该方法(随着引用计数降至0,垃圾收集).
接下来,您C.foo在变量中存储了对未绑定方法的引用.现在不释放内存地址(引用计数为1,而不是0),并通过查找必须使用新内存位置来创建第二个方法实例cobj.foo.因此,您获得两个不同的值.
请参阅以下文档id():
返回对象的"标识".这是一个整数(或长整数),保证在该生命周期内该对象是唯一且恒定的.具有非重叠寿命的两个对象可以具有相同的
id()值.CPython实现细节:这是内存中对象的地址.
强调我的.
您可以通过__dict__类的属性使用对函数的直接引用来重新创建方法,然后调用__get__descriptor方法:
>>> 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>>
Run Code Online (Sandbox Code Playgroud)
请注意,在Python 3中,已经删除了整个未绑定/绑定方法的区别; 你得到一个函数,在你得到一个未绑定的方法之前,以及一个方法,否则,方法总是绑定:
>>> 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>>
Run Code Online (Sandbox Code Playgroud)
此外,Python的3.7增加了一个新的LOAD_METHOD- CALL_METHOD操作码对,将替换当前LOAD_ATTRIBUTE- CALL_FUNCTION精确地操作码对,以避免每次创建一个新的方法的对象.这种优化变换executon路径instance.foo()从type(instance).__dict__['foo'].__get__(instance, type(instance))()与type(instance).__dict__['foo'](instance),因此在该实例"手动"直接传递给函数的对象.
添加到@Martijn Pieters非常好的答案:
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
Run Code Online (Sandbox Code Playgroud)