Python中列表生成式和lambda组合使用的时候,你跳坑了吗?我跳了反正。。

坑:

最近看了一篇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 作为一个参数传入就好了。

发布了28 篇原创文章 · 获赞 74 · 访问量 1653

猜你喜欢

转载自blog.csdn.net/CxsGhost/article/details/103697506