Python动态创建方法问题:lambda为何无法返回预期值?
问题分析与解决方案
这是Python里很常见的闭包延迟绑定坑,我来帮你理清楚:
首先看你这段代码:
def fun(ret): return ret class A: def __init__(self): for string in ['a', 'b']: setattr(self, string, lambda: fun(string)) >>> a = A() >>> a.a() 'b'
为什么会返回'b'?
问题出在lambda表达式的延迟绑定特性上:lambda里引用的string变量,并不是在定义lambda的时候就把当前循环的string值存进去,而是在你调用这个lambda的时候,才会去查找string这个变量的当前值。
当循环结束后,string这个变量最后一次被赋值是'b',所以不管你调用a.a()还是a.b(),lambda都会去取这个已经变成'b'的变量值,自然两个方法都返回'b'了。
怎么修复?
有几种简单的方法可以让每个lambda捕获到循环时的当前string值:
方法1:用默认参数绑定当前值
给lambda加一个默认参数,在定义时就把当前的string值传递进去:
def fun(ret): return ret class A: def __init__(self): for string in ['a', 'b']: # 这里s=string会在定义lambda时把当前string的值作为默认参数保存 setattr(self, string, lambda s=string: fun(s)) >>> a = A() >>> a.a() 'a' >>> a.b() 'b'
方法2:使用functools.partial
partial可以帮你提前绑定函数的参数值,避免延迟绑定的问题:
from functools import partial def fun(ret): return ret class A: def __init__(self): for string in ['a', 'b']: setattr(self, string, partial(fun, string)) >>> a = A() >>> a.a() 'a' >>> a.b() 'b'
方法3:用生成函数创建闭包
写一个小的辅助函数,让它帮你生成每个对应的lambda,利用函数的局部变量来捕获当前值:
def fun(ret): return ret class A: def __init__(self): def make_func(s): # s是make_func的局部变量,每次调用都会保存当前的string值 return lambda: fun(s) for string in ['a', 'b']: setattr(self, string, make_func(string)) >>> a = A() >>> a.a() 'a' >>> a.b() 'b'
这几种方法都能解决你的问题,选你觉得最顺手的就行~
内容的提问来源于stack exchange,提问作者hinerk
相关产品推荐
相关产品推荐

