这种特性叫做延迟绑定闭包。
multipliers和下面这个函数是等效的。
def foo():
funcs = []
for i in range(4):
def bar(x):
return x*i
funcs.append(bar)
return funcs
返回的funcs中的bar函数或者multipliers里面的lambda函数都是一个闭包,这个函数被包含在另一个函数内部,并且引用了外部函数的变量,这种情况下,在函数调用时才会去外部函数的变量作用域中搜索变量的真实值。
假如在foo函数的最后把i的值变成5,那么内部函数被调用时查到的值就是5。
def foo():
funcs = []
for i in range(4):
def bar(x):
return x*i
funcs.append(bar)
i = 5
return funcs
print([m(2) for m in foo()])
# [10, 10, 10, 10]
另外,如果用列表生成式实现的话,还有一个小小的区别。
def multipliers():
funcs = [lambda x:x*i for i in range(4)]
i = 5
return funcs
这个函数在Python2下是[10, 10, 10, 10],在Python3下是[6, 6, 6, 6]。这是因为Python2会把列表生成式里面的i泄露到multipliers的变量域中,而Python3不会。
Python 2.7.10 (default, Feb 7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> [i for i in range(4)]
[0, 1, 2, 3]
>>> i
3
Python 3.6.0 (default, Mar 2 2018, 18:27:18)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> [i for i in range(4)]
[0, 1, 2, 3]
>>> i
Traceback (most recent call last):
File "", line 1, in
NameError: name 'i' is not defined
假如你希望得到[0, 2, 4, 6]这种结果,你应该这么写
def multipliers():
# 把i当做参数传进去
return [lambda x,i=i:x*i for i in range(4)]
或者构造偏函数
from functools import partial
def multipliers():
return [partial(lambda a,b:a*b, i) for i in range(4)]