Python中setattr设置对象__call__方法无效的原因及解决方法
为什么obj()和obj.call()在setattr后不等价?如何运行时设置__call__方法?
先看你给出的测试代码:
class A: def __call__(self): return 1 a = A() print(a()) # 输出1 print(a.__call__()) # 输出1 setattr(a, '__call__', lambda: 100) print(a()) # 还是输出1 print(a.__call__()) # 输出100
原因分析
Python里的特殊方法(双下划线包裹的方法,比如__call__、init),调用时是直接从实例的类(或类的继承链)中查找,不会去实例自身的属性字典里找。
当你写a()时,Python实际执行的逻辑是type(a).__call__(a)——也就是去A类里找__call__方法,完全不看实例a自身的__call__属性。
但当你直接写a.__call__()时,Python会先在实例a的属性里找__call__,找到了你用setattr设置的lambda,所以执行的是这个新函数。
这就是两者结果不一致的核心原因。
解决方法:运行时设置实例的__call__方法
要让a()调用新的逻辑,你需要修改实例所属类的__call__方法,或者给这个实例单独创建一个动态类:
方法1:修改原类的__call__
直接修改A类的__call__,这样所有A的实例都会受影响:
class A: def __call__(self): return 1 a = A() A.__call__ = lambda self: 100 # 修改类的__call__ print(a()) # 输出100
方法2:给单个实例创建动态子类
如果只想让某个实例生效,不影响其他实例,可以给该实例动态生成一个子类,替换它的__class__属性:
import types class A: def __call__(self): return 1 a = A() # 创建一个继承自A的动态子类,重写__call__ DynamicClass = types.new_class('DynamicA', (A,), {}, lambda ns: ns.update({'__call__': lambda self: 100})) a.__class__ = DynamicClass print(a()) # 输出100
或者更简单的写法:
class A: def __call__(self): return 1 a = A() # 临时子类 class TempA(A): def __call__(self): return 100 a.__class__ = TempA print(a()) # 输出100
这样修改后,a()就会执行新的__call__逻辑,和a.__call__()的结果一致了。
内容的提问来源于stack exchange,提问作者Денис Швецов
相关产品推荐
相关产品推荐

