Python类与实例Monkey Patch差异:为何实例补丁会消耗首个参数?
Python类与实例猴子补丁的参数差异解析
核心原因:方法绑定机制的区别
Python里,类定义的方法本质是普通函数,但当通过实例访问类方法时,Python会自动把这个函数包装成绑定方法(bound method)——调用时会自动把实例本身作为第一个参数(也就是self)传入。
但如果你直接给实例的属性赋值一个普通函数(比如my_c.f = f_monkey_patched),这个函数不会被自动包装成绑定方法,它就是个普通的函数对象。此时调用my_c.f(1,2,c=3)时,Python不会自动传入实例my_c作为self,而是把你传入的第一个参数1直接传给了函数的self参数,剩下的2才进入args,这就导致了看起来“首个参数被消耗”的现象。
两种补丁场景对比
1. 给类打补丁的情况
当你修改C.f = f_monkey_patched时,这个函数被挂载到类上,成为类的方法。当实例my_c访问my_c.f时,Python依然会把它包装成绑定方法,调用时自动传入my_c作为self,所以你传入的1,2,c=3会被正确解析为args=(1,2)和kwargs={'c':3},和原方法行为一致。
代码示例:
class C: def f(self, a, b, c=None): print(f"Real f called with {a=}, {b=} and {c=}.") def f_monkey_patched(self, *args, **kwargs): print(f"Patched f called with {args=} and {kwargs=}.") C.f = f_monkey_patched my_c = C() my_c.f(1, 2, c=3) # Output: Patched f called with args=(1, 2) and kwargs={'c': 3}.
2. 给单个实例打补丁的情况
直接给实例赋值普通函数时,没有绑定过程,函数会把调用时的第一个参数当作self。如果要让它和原方法一样自动传入实例,需要用types.MethodType把函数绑定到实例上:
修正后的代码:
import types class C: def f(self, a, b, c=None): print(f"Real f called with {a=}, {b=} and {c=}.") def f_monkey_patched(self, *args, **kwargs): print(f"Patched f called with {args=} and {kwargs=}.") my_c = C() # 用MethodType将函数绑定到实例 my_c.f = types.MethodType(f_monkey_patched, my_c) my_c.f(1, 2, c=3) # Output: Patched f called with args=(1, 2) and kwargs={'c': 3}.
这样修改后,调用my_c.f时就会自动把my_c作为self传入,参数解析恢复正常。
总结
- 类上的方法会在实例访问时自动绑定,调用时自动传入
self - 直接给实例赋值普通函数,不会触发绑定逻辑,函数会把调用时的第一个参数当作
self - 给单个实例打补丁时,需要用
types.MethodType手动绑定函数到实例,才能获得和类方法一致的参数行为
内容的提问来源于stack exchange,提问作者Roman Pavelka
相关产品推荐
相关产品推荐

