如何使用列表推导生成不同lambda函数的列表?

时间:2015-07-02 14:13:28

标签: python python-2.7 lambda list-comprehension

这个问题是从涉及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.

我看到索引变量是通过引用解释的。问题是如何重新措辞,让它按价值处理。

4 个答案:

答案 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)]