这个问题是从涉及Tkinter按钮的回调函数的原始应用程序中提炼出来的。这是说明行为的一行。
lambdas = [lambda: i for i in range(3)]
如果你然后尝试调用生成的lambda函数:
lambdas[0]()
,lambdas[1]()
和lambdas[2]()
都返回2.
理想的行为是让lambdas[0]()
返回0,lambdas[1]()
返回1,lambdas[2])()
返回2.
我看到索引变量是通过引用解释的。问题是如何重新措辞,让它按价值处理。
答案 0 :(得分:5)
使用带默认值的参数将i
的当前值绑定到局部变量。在没有参数的情况下调用lambda
时,会为局部变量i
分配默认值:
In [110]: lambdas = [lambda i=i: i for i in range(3)]
In [111]: for lam in lambdas:
.....: print(lam())
.....:
0
1
2
当i
不是局部变量时,Python looks up its value in the enclosing scope。找到的值是列表推导的for循环中获得的最后一个值i
。这就是为什么在没有带默认值的参数的情况下,每个lambda
返回2,因为在lambdas被调用时for循环已经完成。
解决此问题的另一种常见方法是使用闭包 - a function that can refer to environments that are no longer active ,例如外部函数的本地命名空间,即使该函数已返回。
def make_func(i):
return lambda: i
lambdas = [make_func(i) for i in range(3)]
for lam in lambdas:
print(lam())
打印
0
1
2
这是有效的,因为lam()
被调用,因为i
中的lambda
function的body不是局部变量,Python查找i
的值
函数make_func
的封闭范围。它的本地名称空间仍然是
即使lam
已经存在,也可以使用make_func
完成。该本地名称空间中的值是传递给的值
make_func
,幸运的是,i
的期望值。
作为already mentioned by mkrieger1,
另一种创建新函数的方法,其中已经提供了一些参数值
是使用functools.partial
:
lambdas = [functools.partial(lambda x: x, i) for i in range(3)]
答案 1 :(得分:4)
这里要理解的重要一点是,在评估列表推导期间创建函数,但git config --global --unset <optionname>
的值仅在函数执行期间进行评估。
因此,在您的情况下,您创建了三个函数,并且所有函数都引用了i
。在运行时调用这些函数时,i
将具有值i
,因为这是在2
循环的最后一次迭代中绑定到i
的最后一个值。 / p>
相反,您需要在创建的每个函数中保留for
的当前值。所以,通常的做法是包含一个默认参数,比如这个
i
现在,>>> lambdas = [lambda i=i: i for i in range(3)]
>>> lambdas[0]()
0
>>> lambdas[1]()
1
>>> lambdas[2]()
2
创建一个默认参数lambda i=i: i
,它将具有循环变量i
的当前值。因此,当执行函数时,引用的i
实际上是传递给函数的参数,而不是循环变量。
为避免混淆,您可以选择为变量使用不同的名称,例如
i
如果您正在寻找其他方法来避免这种情况,您可以使用>>> lambdas = [lambda t=i: t for i in range(3)]
函数将数字应用于另一个函数,该函数将使用当前值生成新函数,如下所示
map
此处,>>> lambdas = map(lambda x: lambda: x, range(3))
>>> lambdas[0]()
0
>>> lambdas[1]()
1
>>> lambdas[2]()
2
创建一个匿名函数,该函数接受单个参数lambda x: lambda: x
,并返回另一个函数,该函数不接受任何参数但返回值x
。
注意:请勿在实际代码中使用此x
表单。它可能会降低代码的可读性。
答案 2 :(得分:2)
您可以使用functools.partial
通过部分应用程序从更常规的函数创建专用函数,从而将参数计数减少一个:
from functools import partial
lambdas = [partial(lambda x: x, i) for i in range(3)]
这里,lambda x: x
是带有一个参数的通用标识函数,你创建它的三个特化,不带参数,并返回固定值。
答案 3 :(得分:-1)
啊,进一步的谷歌搜索找到了一个解决方案(诚然,我不会偶然发现自己)。可以使用默认参数调用所需的行为:
lambdas = [lambda i=i: i for i in range(3)]