为何猴子补丁修改类的`__new__`方法并非总能生效?
__new__方法并非总能生效? 这个问题其实戳中了Python方法绑定机制的一个小细节,我来给你一步步掰明白~
先回顾下你遇到的两种矛盾场景:
- 直接把
A.__new__设为object.__new__,实例化完全正常; - 先把
A.__new__替换成自定义的mynew,再改回object.__new__,就会抛出TypeError,说object.__new__()只接受一个参数。
核心原因:方法绑定的差异
Python里的方法不是孤立的函数——当你把一个函数赋值给类的属性时,Python会根据上下文自动处理绑定逻辑:
直接赋值
object.__new__的情况:
当你直接执行A.__new__ = object.__new__时,Python会识别出这是要把父类的__new__方法继承给子类,此时object.__new__会被绑定为A类的类方法。调用A(10)时,Python会自动把A作为第一个参数cls传给object.__new__,剩下的参数会被忽略(因为object.__new__本来就只需要cls),所以实例化正常。先替换自定义函数再改回的情况:
当你先把A.__new__设为普通函数mynew后,类的__new__属性就变成了一个未绑定的普通函数。此时再执行A.__new__ = object.__new__,你其实是把object类里的未绑定函数直接赋值给了A.__new__——这个函数不会自动绑定到A类上,调用A(10)时,Python会把A和10两个参数一起传给它,而object.__new__只接受cls一个参数,自然就报错了。
怎么正确重置__new__方法?
有两个靠谱的解决方案:
方案1:提前保存原始的__new__
在进行猴子补丁之前,先把类原本的__new__方法存起来,重置的时候直接用保存的版本:
class A: def __init__(self, x): self.x = x # 提前备份原始方法 original_new = A.__new__ def mynew(cls, *args, **kwargs): print("Calling mynew") return object.__new__(cls) # 打补丁 A.__new__ = mynew A(10) # 正常输出:Calling mynew # 重置回原始版本 A.__new__ = original_new A(10) # 正常实例化,无报错
方案2:用包装函数适配参数
如果没提前备份,也可以用一个lambda或者包装函数,把object.__new__包装成能接受任意参数的形式,只把cls传给它:
# 错误重置方式:直接赋值object.__new__ # A.__new__ = object.__new__ # 正确重置方式:用包装函数适配参数 A.__new__ = lambda cls, *args, **kwargs: object.__new__(cls) A(10) # 正常工作
这样就能避开方法绑定的坑,顺利完成重置啦~
内容来源于stack exchange

