每当您通过instance.name
(和在Python 2中class.name
)通过查找方法时,都会重新创建该方法对象。Python每次都使用描述符协议将函数包装在方法对象中。
因此,当您查找时id(C.foo)
,将创建一个新的方法对象,您将检索其ID(内存地址),然后 再次丢弃该方法对象 。然后,您查找id(cobj.foo)
,创建了一个新方法对象,该对象重新使用了现在释放的内存地址,并且看到了相同的值。然后再次丢弃该方法(当参考计数降至0时收集垃圾)。
接下来,您将对C.foo
未绑定方法的引用存储在变量中。现在不释放内存地址(引用计数为1,而不是0),然后通过查找必须使用新内存位置的方法来创建第二个 方法实例cobj.foo
。因此,您将获得两个不同的值。
请参阅有关文档id()
:
返回对象的“身份”。这是一个整数(或长整数),在此对象的生存期内,此整数保证是唯一且恒定的。 。
:这是对象在内存中的地址。
强调我的。
您可以通过__dict__
类的属性使用对函数的直接引用来重新创建方法,然后调用__get__
描述符方法:
>>> 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>>
请注意,在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>>
此外,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)
“手动”,所以在该实例通过直接向功能对象。