You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 15:12:47