lambda函数如何在python中引用它的参数?

时间:2011-03-04 05:44:14

标签: python lambda functional-programming closures

我是Python新手。我的任务很简单 - 我需要一个可以用来批量处理的函数列表。所以我玩了一些像

这样的例子
fs = [lambda x: x + i for i in xrange(10)]

令人惊讶的是,

的召唤
[f(0) for f in fs]

给了我[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]的结果。这不是我的预期,因为我希望变量i在不同的函数中具有不同的值。

所以我的问题是:

  1. lambda中的变量i是全局还是本地?

  2. python是否与javascript中的'closure'具有相同的概念?我的意思是这里的每个lambda都包含对i变量的引用,或者它们只包含每个i的值的副本吗?

  3. 如果我希望在这种情况下输出为[0, 1, .....9],我该怎么办?

4 个答案:

答案 0 :(得分:10)

它看起来有点乱,但你可以通过做这样的事情得到你想要的东西:

>>> fs = [(lambda y: lambda x: x + y)(i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

通常,Python支持“闭包”概念,类似于您在Javascript中使用的概念。但是,对于列表解析中的lambda表达式的特定情况,似乎i只绑定一次并连续接受每个值,使每个返回的函数充当虽然i是9.上面的hack使用捕获的i值显式地将y的每个值传递给一个返回另一个lambda的lambda。

答案 1 :(得分:6)

您遇到的问题是“早期绑定”和“后期绑定”之间的区别。

当Python从外部范围(在这种情况下为i)中查找变量时,它使用后期绑定。这意味着它在函数调用时看到该变量的值,而不是函数定义时的值。

因此,在您的示例代码中,所有10个lambda函数都会看到循环过程分配给i变量的最终值:9

Greg的答案显示了一种强制早期绑定行为的方法(即创建一个额外的闭包并在循环内部立即调用它)。

强制早期绑定语义的另一种常用方法是“默认参数hack”,它在函数定义时将变量绑定为默认参数:

>>> fs = [(lambda x, _i=i: x + _i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

两种方法都有效。 Greg的优点是不会弄乱返回的函数的签名,默认参数hack比定义命名函数而不是使用lambda表达式时添加额外的闭包级别更快且更具可读性。

答案 2 :(得分:4)

  1. 变量i是列表推导的本地变量,但它可用于lambda,因为lambda在其范围内。
  2. 是的,lambdas是封闭的。变量绑定可能并不总是按照您希望的方式工作,但它们是闭包。不过,你不应该过分依赖它们。
  3. 您只想循环xrange(10)。你可以用lambdas做到这一点(见另一个答案),但你不想这样做。应该非常谨慎地使用Lambda。
  4. lambda以这种方式运行的原因是因为每个循环i都会反弹。由于我不是lambda的本地,它也会改变,它持有的最后一个值是9.所以你所做的只是0 + 9 10次。

答案 3 :(得分:1)

我是本地人,蟒蛇确实有封闭。

我相信你的困惑在于你将fs命名为相同功能的列表。

>>> fs = [lambda x: x + i for i in xrange(10)]
>>> fs
[<function <lambda> at 0x02C6E930>, <function <lambda> at 0x02C6E970>, <function <lambda> at 0x02C6E9B0>, <function <lambda> at 0x02C6E9F0>, <function <lambda> at 0x02C6EA30>, <function <lambda> at 0x02C6EA70>, <function <lambda> at 0x02C6EAB0>, <function <lambda> at 0x02C6EAF0>, <function <lambda> at 0x02C6EB30>, <function <lambda> at 0x02C6EB70>]
>>> fs[0](0)
9
>>> fs[0](100)
109
>>> fs[5](0)
9
>>> fs[5](100)
109

我认为返回列表的单个函数更合适。

>>> fs3 = lambda x: [x + i for i in xrange(10)]
>>> fs3
<function <lambda> at 0x02C6EC70>
>>> fs3(0)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]