Python动态为类添加方法未达预期效果问题求助
问题分析与解决方案
这是个典型的闭包延迟绑定问题,我来给你拆解一下原因和解决办法:
为什么会出现这个问题?
你在循环里定义的_method是一个闭包函数,它引用了循环变量o。但闭包的特性是:它不会在定义时就捕获o的当前值,而是在函数被调用的时候才去查找o的当前值。当你循环结束后,o已经指向了最后一次迭代的键值对(在你的例子里是('w','w')),所以三个动态添加的方法调用时,都会取到这个最后值,导致输出全是w。
几种可行的解决方案
方案1:用默认参数捕获当前值
修改循环内的函数定义,把o作为默认参数传入——默认参数是在函数定义时计算并绑定的,这样每次循环都会把当前的o保存到函数的默认参数里:
d = dict(q='q', w='w', e='e') class A(object): pass for o in d.items(): print o def _method(self, o=o): # 这里用默认参数捕获当前的o return o[0] setattr(A, o[1], _method) a = A() print a.q() print a.w() print a.e()
方案2:用functools.partial绑定参数
如果你的方法逻辑更复杂,可以用functools.partial来提前绑定参数,避免闭包的延迟绑定问题:
import functools d = dict(q='q', w='w', e='e') class A(object): pass # 先定义基础方法 def _base_method(self, o): return o[0] for o in d.items(): print o # 绑定当前的o到方法参数中 bound_method = functools.partial(_base_method, o=o) setattr(A, o[1], bound_method) a = A() print a.q() print a.w() print a.e()
方案3:用lambda的默认参数(适合简单逻辑)
如果方法逻辑很简单,也可以用lambda表达式配合默认参数来实现,原理和方案1一致:
d = dict(q='q', w='w', e='e') class A(object): pass for o in d.items(): print o setattr(A, o[1], lambda self, o=o: o[0]) a = A() print a.q() print a.w() print a.e()
额外说明
注意在Python 2中,普通字典是无序的,所以d.items()的迭代顺序可能和你定义的顺序不一致。如果需要固定迭代顺序,可以改用collections.OrderedDict。
内容的提问来源于stack exchange,提问作者Nannan AV
相关产品推荐
相关产品推荐

