解释下面代码结果,为什么不是[0, 2, 4, 6]?
def index():
return [lambda x: i * x for i in range(4)]
print([m(2) for m in index()])
# 输出结果:[6,6,6,6]
知识点:闭包原理 、名称空间与作用域
def index():
return [lambda x: i * x for i in range(4)]
# index函数返回的是一个列表是生成式,返回的是一个列表,该列表包含四个匿名函数,各匿名函数依次返回的是 3*x,3*x,3*x,3*x
print([m(2) for m in index()])
# 这行代码又是一个列表生成式,等价于下面的for循环+列表框起来
# for m in index():
# print(m(2))
详解:为什么index 每次返回的都是3*x的匿名函数?
# 上面index等价于下面
def index():
index_list = []
for i in range(4):
def foo(x):
return x*i
index_list.append(foo)
return index_list
# 原理:i 在外层作用域
"""
1. index函数依次从(0,1,2,3)中,for循环
2. 每次for循环中,做了两件事:1.只定义了一个函数,2.把函数追加进列表中
3. 每次定义的foo函数,声明的名称空间只有 参数x,而foo内部需要变量i,调用时,发现自己没有定义,就需要去外部函数index中去寻找。
4. 由于每次for循环只是定义函数,没有调用函数,所以每次for循环i的值不会直接给到foo函数中,只有在foo函数调用时,foo函数才会去获取i的值。
5. 而foo函数调用,是在index函数定义完成之后的每次for循环打印m(2)时,此时index函数内部for循环已经完成,i由0遍历到3。
6. foo函数调用,内部需要变量i,但foo函数内部没有,就需要去外部函数index2中去寻找,此时index函数的名称空间有i,且i的值固定为3。
"""
解决办法
# 解决原理:变闭包作用域为局部作用域
# 若想index函数,返回的是[ 0*x,1*x,2*x,3*x],只需要定义匿名函数时,定义参数i,并每次for循环的i 给到匿名函数
def index():
return [lambda x,i=i: i * x for i in range(4)]
# 等价于下面
def index():
index_list = []
for i in range(4):
def foo(x, i=i):
return x*i
index_list.append(foo)
return index_list
# 这样,每次定义的foo函数,声明的名称空间:参数x和i,且i的值由每次的for循环传递进来,就不需要去外部函数index2中去寻找。