Python:for循环+setattr重载运算符异常问题及解决方法
问题原因与修正方案
错误代码示例
你大概率写了类似这样的代码:
class X: def __init__(self, val): self.val = val # 定义运算符对应的函数 ops = [ ('__add__', lambda self, other: self.val + other.val), ('__mul__', lambda self, other: self.val * other.val) ] # 循环绑定方法 for name, func in ops: setattr(X, name, func)
运行后X(5)+X(2)和X(5)*X(2)都返回10,不符合预期。
问题根源
这是Python闭包的延迟绑定特性导致的:lambda表达式里引用的循环变量func,不会在循环迭代时立即获取当前值,而是等到函数被调用时,才去查找当前作用域中func的最终值。循环结束后,func最后指向的是乘法对应的lambda,所以__add__和__mul__实际上都绑定了同一个乘法函数,自然加法运算也会执行乘法逻辑。
修正方法
方法1:利用默认参数即时绑定
修改循环逻辑,给lambda添加默认参数,在迭代时就把当前的func值绑定进去:
class X: def __init__(self, val): self.val = val ops = [ ('__add__', lambda self, other: self.val + other.val), ('__mul__', lambda self, other: self.val * other.val) ] for name, func in ops: # 通过默认参数f绑定当前迭代的func setattr(X, name, lambda self, other, f=func: f(self, other))
方法2:使用functools.partial固定参数
借助functools.partial将当前的函数作为固定参数传入,生成独立的可调用对象:
from functools import partial class X: def __init__(self, val): self.val = val def wrap_method(func, self, other): return func(self, other) ops = [ ('__add__', lambda self, other: self.val + other.val), ('__mul__', lambda self, other: self.val * other.val) ] for name, func in ops: setattr(X, name, partial(wrap_method, func))
方法3:提前生成绑定好的方法列表
通过列表推导式提前创建每个方法,利用默认参数完成绑定:
class X: def __init__(self, val): self.val = val ops = [ ('__add__', lambda self, other: self.val + other.val), ('__mul__', lambda self, other: self.val * other.val) ] # 提前生成每个绑定好的方法 methods = [(name, lambda self, other, f=func: f(self, other)) for name, func in ops] for name, method in methods: setattr(X, name, method)
验证效果
修正后运行:
print(X(5) + X(2)) # 输出7 print(X(5) * X(2)) # 输出10
符合预期。
内容的提问来源于stack exchange,提问作者Tachy0n_
相关产品推荐
相关产品推荐

