坑:
最近看了一篇blog,在里面看到一段挺有意思的代码。
自认为基础学的很扎实的我,第一反应就认为输出结果肯定是错的
代码如下:
def num():
return [lambda x:i*x for i in range(4)]
print([m(1) for m in num()])
输出结果:[3, 3, 3, 3]
代码很简单,就是生成4个匿名lambda函数,然后用循环一个个取出来调用,并给x赋值1
看到这个代码第一反应是这样的
[lambda x: 0, lambda x: x, lambda x: 2*x, lambda x: 3*x]
期望输出结果:
[0, 1, 2, 3]
但实际上经过测试,输出确实是[3, 3, 3, 3]
原因:
且听我细细道来
先看下面的代码:
def num(i):
return lambda x: i*x
a = [num(i) for i in range(4)]
for j in a:
print(j(1))
输出结果:
0
1
2
3
把lambda放在一个函数中,并给这个函数一个参数 i,就可得到我们期望的结果。
这是因为,列表生成式生成函数时,所有位置参数必须有值传入,否则就会报错!!!所以此时会在命名空间寻找 i 的值
比如下面的代码,我加了一个参数 k,但是以下两种写法都会报错!!!
def num(i, k):
return lambda x: i*x, k
a = [num(i) for i in range(4)] #报错
a = [num(i, k) for i in range(4)] #报错
到这里大家先记住,这里的 i 是一个参数!!
接下来再看我们最开始的lambda
return [lambda x:i*x for i in range(4)]
你会发现,这里的 i 不是参数,而是一个变量
对于一个变量,在函数生成时并不需要即时传入,只有调用函数时,变量才会被被用到
请看,以下代码是可以顺利运行的,也就是当函数调用时,才会去命名空间寻找 k
k = "我是变量"
def num(i):
return lambda x: i*x, k
a = [num(i) for i in range(4)]
所以说到这里,问题就很明显了
这段代码中的i是一个变量,只有lambda函数被调用时才会去命名空间寻找 i 的值
而此时经过循环,i 的值已经变成 3 ,所以四个lambda函数是一样的
def num():
return [lambda x:i*x for i in range(4)]
print([m(1) for m in num()])
要解决这个问题,可以这样写
def num():
return [lambda x, i=i:i*x for i in range(4)]
print([m(1) for m in num()])
把 i 作为一个参数传入就好了。