Python 闭包相关之late binding机制

先上代码:

1 def listFuc():
2     temp = [lambda x: i*x for i in range(4)]
3     return temp
4 for singleFuc in listFuc():
5     print (singleFuc(2))

上面这段代码输出结果是什么呢?

我们可能会想:listFuc这个函数是将temp这个list返回,而temp里面的元素是匿名函数,匿名函数传入参数为x,返回值为 i*x, i依次为0,1,2,3. 所以在下面这个for循环里面,将2依次传入这4个匿名函数里对x赋值,print输出返回的结果,所以结果是 0*x,1*x,2*x,3*x,即 0 2 4 6

但结果确是:

6
6
6
6

为什么呢?

原因在于Python的迟绑定(late binding)机制。

闭包中内部函数的值只有在被调用时才会进行查询。

首先此匿名函数传入参数只有x,虽然返回的是i*x,但是现在传入参数中并没有i,没有对匿名函数内部的i赋值,所以相当于你申明了返回了4个函数:

lambda x: i*x
lambda x: i*x
lambda x: i*x
lambda x: i*x
for i in range(4)这句代码起到的作用仅仅是循环4次得到4个如上面那样的匿名函数。

因此等到listFuc函数返回的lambda函数被调用时,会在附近的作用域中查询变量i的值,而在listFuc生成返回数组之后,i的值是3,因此singleFuc实际上都是:

lambda x: 3*x

所以最后输出的结果就是 6666 了

666666

那么要输出 0 2 4 6时,解决办法是在定义匿名函数时,就将i的值作为参数传入匿名函数内部:

1 def listFuc():
2     temp = [lambda x,i=i: i*x for i in range(4)]
3     return temp
4 for singleFuc in listFuc():
5     print (singleFuc(2))

这样,在每次创建匿名函数时,实际上是这样的过程:

lambda x,i=0: i*x

lambda x,i=1: i*x

lambda x,i=2: i*x

lambda x,i=3: i*x

所以singleFuc每次传入参数2作为x的值时,i都已经被赋了值。

for i in range(4)这句代码的作用就不仅是循环4次得到4个匿名函数了,还充当了对匿名函数内部作用域变量赋值的作用
这样写可能会更好理解:
1 def listFuc():
2     temp = [lambda x,y=i: y*x for i in range(4)]
3     return temp
4 for singleFuc in listFuc():
5     print (singleFuc(2))

结果就是:

0
2
4
6

参考:http://www.cnblogs.com/harelion/p/5577087.html

猜你喜欢

转载自www.cnblogs.com/Anythingkk/p/9000871.html