Python用lambda函数封闭范围变量

Posted

技术标签:

【中文标题】Python用lambda函数封闭范围变量【英文标题】:Python enclosing scope variables with lambda function 【发布时间】:2017-04-21 04:47:52 【问题描述】:

我写了这个简单的代码:

def makelist():
  L = []
  for i in range(5):
    L.append(lambda x: i**x)
  return L

好的,现在我打电话

mylist = makelist()

因为稍后调用嵌套函数时会查找封闭范围变量,所以它们都有效地记住了相同的值: 因此,我希望找到循环变量在最后一次循环迭代中的值,但是当我检查我的列表时,我看到:

>>> mylist[0](0)
1
>>> mylist[0](1)
4
>>> mylist[0](2)
16
>>> 

我很困惑,为什么我的代码没有保留最后一个 for 循环值?为什么我不必使用这样的默认参数显式保留封闭范围值:

L.append(lambda x, i=i: i ** x)

提前致谢

【问题讨论】:

【参考方案1】:

我会尽力把它分解成更简单的术语

第一 -

for 循环正在循环并将 'uncall' lambda 函数附加到列表中。 --在您的示例中,它将执行 5 次。

第二次

现在.. AFTER for 循环完成了它的迭代。for 循环中“i”的值将是“4”。 -- lambda 函数不包含“i”的任何值,因为您还没有调用它。

第三次

当您调用 lambda 函数时,只有函数会在循环完成了它的迭代;这是“4”

第 4 次

Lambda 然后插入“4”作为参数来进行计算。

【讨论】:

【参考方案2】:

尽管i 随着时间的推移采用多个值,但实际上只有一个变量ii 的内容在循环期间被更改。但是闭包捕获的是变量,而不是值。在您调用它之前,不会在 lambda 内部进行任何评估。在您调用该函数时,您会访问i 的当前值,它恰好是最后一个值。

至于为什么i=i 解决了这个问题,这在The Hitchhiker's guide to Python 中有解释(Common Gotchas):

Python 的默认参数在定义函数时计算一次,而不是每次调用函数时(就像在 Ruby 中一样)。这意味着,如果您使用可变的默认参数并对其进行变异,那么您将并且已经对该对象进行了变异,以便将来调用该函数。

因此,在您创建的闭包内部发生的每个新绑定(并且恰好被命名为i,就像外面的绑定一样)在创建闭包时计算其默认值。因此,您拥有“正确”的值,可以在调用闭包时使用。

【讨论】:

【参考方案3】:

首先,观察列表中的所有 5 个函数都是相同的,因为它们都使用 makelist 内部的 finalmakelist

但是,

i 是在评估 lambda 表达式时创建的 闭包 的一部分。这意味着如果您在从makelist 调用函数的范围内为i 分配一个新值,这不会影响任何函数。他们从作为闭包结果附加到函数的命名空间中查找i 的值,而不是从全局范围中查找。

【讨论】:

以上是关于Python用lambda函数封闭范围变量的主要内容,如果未能解决你的问题,请参考以下文章

在快速滑块中使用封闭范围的变量

C++ lambda表达式(函数指针和function)

Lambda 表达式和变量捕获

使用lambda函数进行向量排序,当不在同一范围内时如何传递变量来捕获组?

JavaScript中的“闭包”

列表推导中定义的变量是不是会泄漏到封闭范围内? [复制]