每当您通过
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__描述符方法:
>>> 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)“手动”,所以在该实例通过直接向功能对象。



