Python中类方法与实例方法的id差异及绑定机制疑问
Python中类方法与实例绑定方法的id差异解析
核心疑问
示例代码中,id(p.name_as_upper)与id(p2.name_as_upper)结果相同,但和id(Person.name_as_upper)结果不同;同时修改Person.name_as_upper后,实例p调用该方法的结果也随之变化。需要澄清类方法与实例关联方法的底层逻辑。
示例代码
class Person: def __init__(self, name: str) -> None: self.name = name def name_as_upper(self) -> str: return self.name.upper() p = Person("john") p2 = Person("sarah") print( id(p.name_as_upper), id(p2.name_as_upper), id(Person.name_as_upper) ) # first two are the same, last one different print(p.name_as_upper()) # JOHN print(Person.name_as_upper(p)) # JOHN Person.name_as_upper = ( lambda self: self.name.upper() + " - suffix" ) # "point" name_as_upper method object to different method print(p.name_as_upper()) # JOHN - suffix print(Person.name_as_upper(p)) # JOHN - suffix
原理澄清
1. 类函数与实例绑定方法的本质区别
Person.name_as_upper是类中定义的原始函数对象,它独立于任何实例,属于类的属性。- 当你访问
p.name_as_upper或p2.name_as_upper时,Python会动态生成一个绑定方法(bound method)对象——这个对象把实例(比如p)和类的函数(Person.name_as_upper)绑定在一起,调用时会自动把实例作为第一个参数传入函数。
2. 实例绑定方法的id为何看似相同
示例中id(p.name_as_upper)和id(p2.name_as_upper)相同,是因为Python的内存回收机制:当你获取第一个绑定方法后,它没有被引用,很快被销毁;获取第二个绑定方法时,复用了之前释放的内存地址,所以id值巧合一致。实际上,每次访问实例的方法属性,都会生成一个新的绑定方法对象。
3. 修改类方法后实例方法随之变化的原因
实例本身并不会存储方法的副本,当你访问p.name_as_upper时,Python会优先从实例自身的属性中查找,找不到就去类的属性里找对应的函数,再动态生成绑定方法。因此,当你修改Person.name_as_upper指向新的函数后,下次访问实例的该方法时,会用新函数生成绑定方法,调用结果自然随之改变。
4. 调用等价性的本质
p.name_as_upper()等价于Person.name_as_upper(p),是因为绑定方法在调用时,会自动将绑定的实例作为第一个参数传递给类的函数,这和手动把实例传给类函数的效果完全一致。
内容的提问来源于stack exchange,提问作者jensa
相关产品推荐
相关产品推荐

