Python中两类gen函数返回lambda的执行结果差异解析求助
为什么两个类似的lambda生成代码会得到不同结果?
嘿,这个问题可是Python里闭包延迟绑定的经典“坑”,我来给你讲明白其中的门道~
先看第一个列表推导式的情况
你写的第一段代码:
def gen(): return [lambda x: x + i for i in range(4)] print([m(1) for m in gen()])
执行结果是[4, 4, 4, 4],核心原因在于lambda的延迟绑定特性:
- lambda函数里引用的变量
i,并不是在定义lambda的时候就固定下来,而是在实际调用lambda的时候,才会去查找i的当前值。 - 列表推导式是一次性完成所有lambda的创建的——当整个
[lambda x: x+i for i in range(4)]执行完,循环已经跑完,此时变量i的最终值是3(因为range(4)是0、1、2、3)。 - 所以当你后续调用每个
m(1)时,每个lambda都会去取当前的i=3,计算1+3=4,自然四个结果都是4。
再看第二个生成器表达式的情况
第二段代码:
def gen(): return (lambda x: x + i for i in range(4)) print([m(1) for m in gen()])
结果是[1, 2, 3, 4],这要归功于生成器的惰性求值特性:
- 生成器表达式
(lambda x: x+i for i in range(4))不会一次性生成所有lambda,而是每次迭代的时候才生成一个lambda。 - 当你遍历生成器
for m in gen()时,每一次迭代都会对应range(4)里的一个i值:第一次迭代时i=0,此时创建lambda并立刻调用m(1),得到1+0=1;第二次迭代i=1,得到1+1=2,以此类推。 - 因为每次生成lambda时,
i的当前值被捕获到了生成器的迭代上下文里,每个lambda调用时用的都是当时的i值,所以结果符合你的预期。
额外小技巧:如何让第一个列表推导式得到预期结果?
如果想让列表推导式也返回[1,2,3,4],可以用默认参数绑定的方式,把i的值在定义lambda时就固定下来:
def gen(): return [lambda x, i=i: x + i for i in range(4)] print([m(1) for m in gen()]) # 输出 [1,2,3,4]
这里的i=i会在lambda定义时,把当前循环的i值作为默认参数的值绑定到lambda里,后续调用时就不会再去查找全局的i了。
内容的提问来源于stack exchange,提问作者Aaron.Z
相关产品推荐
相关产品推荐

