为何在列表推导式中包含完整lambda表达式?Python闭包解析
为什么要在列表推导式中用完整的lambda表达式解决闭包变量生命周期问题?
嘿,这个问题可是Python闭包里的经典“踩坑现场”,我来给你唠明白~
首先得先搞懂原代码为啥会出意外:
def create_multipliers(): return [lambda x: i * x for i in range(5)] for multiplier in create_multipliers(): print(multiplier(2))
你以为会输出 0, 2, 4, 6, 8,结果全是 8?这是因为lambda里的i不是在创建时绑定值,而是在调用时才去查找变量i的当前值。列表推导式运行的时候,i在循环里从0跑到4,但所有lambda都共享同一个i的引用。等你后续调用这些lambda的时候,循环早就结束了,i已经固定成4了,所以每个lambda计算的都是4*2。
那为啥要在列表推导式里用“完整的lambda表达式”(也就是带默认参数的写法,比如lambda x, i=i: i*x)呢?
这是因为Python的函数默认参数是在函数定义时就完成求值绑定的,而不是调用时。当我们写lambda x, i=i: i*x的时候,每个lambda在创建的瞬间,都会把当时循环里i的当前值作为自己的默认参数存下来。这样每个lambda都持有了属于自己的i的“快照”,而不是共享那个会一直变化的外部变量i。
修正后的代码长这样:
def create_multipliers(): return [lambda x, i=i: i * x for i in range(5)] for multiplier in create_multipliers(): print(multiplier(2))
运行这个代码,就能得到预期的0, 2, 4, 6, 8啦。
当然还有其他办法,比如用functools.partial来绑定参数,但用默认参数的lambda写法是最直接的原生解决方案,本质都是为了把循环变量的当前值固定在lambda创建的时刻,而不是等调用时才去取变量的最终值。
内容的提问来源于stack exchange,提问作者Tom Russell
相关产品推荐
相关产品推荐

