题目:
lst = [lambda x: x*i for i in range(4)]
res = [m(2) for m in lst]
print res
实际输出:[6, 6, 6, 6]
想要输出 [0, 2, 4, 6] 应该怎么改?如下:
方法一: i 重新赋值
lst = [lambda x, i=i: x*i for i in range(4)]
res = [m(2) for m in lst]
print res
如果想得到[0, 100, 200, 300]
这个结果,可以按照下面几种方式来修改multiply
函数。
方法二:使用生成器,让函数获得i
的当前值。
def multiply():
return (lambda x: i * x for i in range(4))
print([m(100) for m in multiply()])
#或者
def multiply():
for i in range(4):
yield lambda x: x * i
print([m(100) for m in multiply()])
方法三:使用偏函数,彻底避开闭包。
from functools import partial
from operator import __mul__
def multiply():
'''partial 函数的功能就是:把一个函数的某些参数给固定住,返回一个新的函数。
'''
return [partial(__mul__, i) for i in range(4)]
print([m(100) for m in multiply()])
这个问题涉及到了Python的闭包及延时绑定的知识(Python作用域)。
在Python核心编程里,闭包的定义如下:
如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认定是闭包。
总结为三点:
1、是一个内嵌函数
2、对外部函数变量引用
3、外部函数返回内嵌函数
简单的闭包例子:
def counter(start_at=0):
count = [start_at]
def incr():
count[0] += 1
return count[0]
return incr
上面的那道题,可以写成这样:
def func():
fun_list = []
for i in range(4):
def foo(x):
return x*i
fun_list.append(foo)
return fun_list
for m in func():
print m(2)
func()是一个包含四个函数的列表:
[<function func at 0x00000000021CE9E8>, <function func at 0x00000000021CEA58>, <function func at 0x00000000021CEAC8>, <function func at 0x00000000021CEB38>]
当我们执行 m(2) 时,运行到foo()内部函数,发现变量 i 并不是foo()中的变量,于是就到外部函数func中寻找变量 i ,但此时外部的 for 已经循环完毕,最后的 i =3 。所以,每次
执行m(2),i 的值都是 3 ,因此,最终结果会是 [6, 6, 6, 6] 。
当在foo()中添加 i=i 后,即:
def func():
fun_list = []
for i in range(4):
def foo(x, i=i):
return x*i
fun_list.append(foo)
return fun_list
for m in func():
print m(2)
- for循环执行时,就已经把 i(0, 1, 2, 3) 的值传给了foo()函数,此时的 i 已经是foo()函数的内部变量,运行到foo()函数时,就不会到外部函数寻找变量 i ,直接运行x*i(0, 1, 2, 3),因此最终结果会是 [0, 2, 4, 6] 。