Python中从类与对象访问函数为何内存地址不同?
关于Python方法绑定与内存地址的疑问
我在阅读Python描述符相关文档时遇到了如下示例代码:
>>> class D(object): ... def f(self, x): ... return x >>> d = D() >>> D.__dict__['f'] # Stored internally as a function <function f at 0x00C45070> >>> id(D.__dict__['f']) # Memory location 49294384 >>> D.f # Get from a class becomes an unbound method <unbound method D.f> >>> id(D.f ) 48549440 >>> d.f # Get from an instance becomes a bound method <bound method D.f of <__main__.D object at 0x00B18C90>> >>> id(d.f) 48549440
我理解Python会将类的函数定义作为独立对象存储在类的__dict__变量中,直接通过__dict__访问时其内存地址为49294384。但为何通过类(D.f)或对象(d.f)访问时,显示的是内存地址为48549440的不同方法对象?按道理不是应该指向__dict__中的同一个对象吗?请问原因是什么?
问题解答
这其实是Python中描述符协议在背后默默工作的结果,咱们一步步拆解来看:
- 首先,Python里用
def定义的函数其实都实现了描述符协议,它们自带__get__方法。当你通过类或者实例去访问这个函数的时候,Python会自动调用这个__get__方法,而不是直接返回__dict__里的原始函数对象。 - 当你用
D.__dict__['f']直接访问时,相当于绕开了描述符的逻辑,拿到的是存储在类命名空间里的原始函数本身,所以它的内存地址是49294384。 - 当你访问
D.f时,Python会调用函数的__get__方法,并且传入instance=None和owner=D,这时候__get__会返回一个**未绑定方法(unbound method)**对象——这个对象是个全新的包装器,它持有对原始函数和类的引用,但本身是独立的新对象,所以内存地址和原始函数不一样。 - 当你访问
d.f时,同样会触发__get__方法,不过这次传入的参数是instance=d和owner=D,返回的是**绑定方法(bound method)**对象——这个包装器会把实例d绑定到函数的第一个参数self上,它也是一个新的对象。 - 至于你看到
D.f和d.f的内存地址相同,这其实是个小巧合:在这个特定的解释器会话里,前一个未绑定方法对象被回收后,它占用的内存刚好被新创建的绑定方法对象复用了,本质上它们是完全不同的方法对象。
简单总结一下:方法对象(不管绑定还是未绑定)都是原始函数的"包装器",每次通过类或实例访问函数时,都会触发描述符的__get__逻辑来创建这个包装器,所以它们的内存地址自然和原始函数不一样。
内容的提问来源于stack exchange,提问作者Shiva Krishna Bavandla
相关产品推荐
相关产品推荐

