python中经典的lambda问题
l=[ lambda: n for n in range(5) ]
for x in l:
print(x())
输出结果:
4
4
4
4
4
后面还有解决方案
l=[ lambda n=i: n for i in range(5) ]
for x in l:
print(x())
输出结果:
0
1
2
3
4
以前我也看得似懂非懂,今天又仔细思考了一下,发现了点端倪。先看一下另一个经典的问题,关于用list做函数参数默认值的问题。
def add( num, l=[]):
l.append(num)
return l
l1=add(1)
l2=add(2)
print(l1)
print(l2)
输出:
[1,2]
[1,2]
解决方案也很简单,不要在参数默认值里创建列表(如果这也算创建的话),把列表的创建放到函数中,如下:
def add(num,l=None):
if not l:
l=list()
l.append(num)
else:
l.append(num)
return l
输出结果大家自己去敲一下代码试一下。关于这个问题的解释是函数参数默认值在函数定义的时候就已经被创建了,等到函数运行时我们只是在多次的引用同一个变量。但这个问题跟刚才的那个有什么关联呢?其实这两种现象是同一个问题。lambda是什么?匿名函数,匿名函数也是函数,只是字面定义方式不一样罢了。
def fun(x):
return x
和
fun=lambda x:x
这两种书写方式应该是等价的(不考虑其他细节)。所以在函数中的默认值问题在lambda中也同样存在,当然也有同样的解决办法。那我们再来看第一个问题,我们把问题的形式转化一下
l=[ lambda:n for n in range(5) ]
for x in l:
print(x())
和以下的形式应该等价:
l=[]
for n in range(5):
l.append(lambda:n)
for x in l:
print(x())
看出问题的关键了吗?我们在l.append(lambda:n)中定义的lambda,其中的n是引用for n in range(5)这一句中的,这只是lambda的定义阶段,lambda并没有执行,等这两句执行完了n已经等于4了,也就是说我们定义的这5个lambda全都成了lambda:4,等到执行的时候自然输出就成了4,4,4,4,4。我们再把解决方案换一种形式写:
l=[]
for i in range(5):
l.append(lambda n=i:n)
for x in l:
print(x())
我们刚才讲了,函数和lambda其实本质是一样的,那么lambda中的参数默认值的效果应该和函数中的参数默认值的效果也是一样的,函数中的参数默认值是在定义的时候创建并保存的,那lambda中的参数默认值一定也一样。所以这5个lambda有了各自不同的参数默认值,而不是去引用同一个。这就又引出了另一个问题,那就是函数中的变量都是在什么时候分析引用的。我们来做个简单的实验
def fun(num,l=x):
l.append(num)
return l
以上函数如果是在交互模式下输入的,应该在函数输入完毕后马上报错告诉你x没有定义,我们在来看看另一种情况。
def fun(n):
print(x)
这个函数输入完毕后系统没有马上报错,但是当你调用的时候会报错,同样也是x没有定义。
这样问题就很明显了,还是那个结论,函数(包括lambda)中的默认参数会在函数定义的时候创建或者引用,而函数体内的变量则要等到调用这个函数时才会创建或者引用(这里的创建、引用是不严谨的描述,我也不知道该用什么词合适,反正大家知道一个是在定义的时候关联到它们,一个是在运行的时候关联到它们就好了)。这样问题就明了了。再回到刚才的两个问题,前者(函数)其实算是被这种现象坑了,我们不得不把默认参数转到函数体内来处理,后者(lambda),我们其实是受益于这种现象,利用这种现象实现了保存lambda中某些变量的功能,所以这也不能完全称为坑,毕竟在某些方面对我们是有利的。以上均为个人分析言论,肯定有欠缺的地方,望大家能批评指正。
阅读(1449) | 评论(0) | 转发(0) |