Lambda 函数在 Python 3 和 Python 2 中的行为不同

Posted

技术标签:

【中文标题】Lambda 函数在 Python 3 和 Python 2 中的行为不同【英文标题】:Lambda functions unequal behaviors in Python 3 and Python 2 【发布时间】:2014-04-12 20:58:08 【问题描述】:

为什么是这个代码

>>> i=1
>>> add_one=lambda x:x+i
>>> my_list=[add_one(i) for i in [0,1,2]]

在 Python 2.7 中抛出这个结果:

>>> my_list
[0, 2, 4]

但是 Python 3.3 中的相同代码会抛出另一个结果:

>>> my_list
[1, 2, 3]

对我来说,Python 2.7 的结果更有意义,因为“i”变量位于调用 lambda 函数的同名范围内。我不明白 Python 的两个分支中 lambdas 函数的这些不平等行为。

【问题讨论】:

【参考方案1】:

不同之处在于,在 Python 3 中,列表推导式不会将其变量泄漏到封闭范围内。您可以看到 Guido here 对此的一些讨论;该帖子包含一个与您的非常相似的示例。

在这两个版本中,您的add_one(i) 是一个引用变量i 的函数。调用函数时,将在封闭范围内按名称查找此变量。由于函数是在全局模块级别定义的,因此“封闭范围”是全局范围。这意味着当您调用 add_one 时,它将使用它在全局范围内找到的任何 i 值(即,它将查找名为 i 的全局变量)。

在 Python 2 中,列表解析将其变量 i 泄漏到封闭范围。由于您的列表理解处于全局模块级范围,“封闭范围”再次是全局的,并且泄漏的变量 i 是一个全局变量,覆盖了您最初使用 i = 1 行设置的值。调用add_one 然后引用这个变量。由于 i 会随着每次迭代而变化,因此您的函数在每次迭代时都会有效地执行 i+i

在 Python 3 中,列表推导式在私有范围内创建自己的变量。但是您的函数仍然访问全局i。由于 Python 3 列表解析不会泄漏到全局范围,因此这个全局 i 永远不会改变,并且您的函数在每次迭代时有效地执行 x+1(其中 x 是列表解析的循环变量)。

【讨论】:

首先,我认为 lambda 函数在声明函数时采用 'i' 的值,而不是在调用函数时。但后来我尝试了这个:>>> i=1 >>> add_one=lambda x:x+i >>> i=5 >>> my_list=[add_one(i) for i in [0,1,2]] 和“my_list”扔了:[5, 6, 7]。然后我对 Python3 中 lambda 函数的行为感到惊讶。您对列表竞争范围的解释澄清了一切。用 GvR 的话来说:列表推导中的变量会暂时隐藏,但不会覆盖周围范围内的同名变量。 @Trimax:人们经常对函数关闭变量的方式感到困惑。但它本质上必须以这种方式工作。如果函数在定义它们时获取变量的值,则不可能编写以普通方式(即在调用时查找)使用普通全局变量的函数。

以上是关于Lambda 函数在 Python 3 和 Python 2 中的行为不同的主要内容,如果未能解决你的问题,请参考以下文章

什么是lambda函数?它有什么好处?

python lambda函数用法?

Python基础(十九):函数加强

Python 3 之 lambda匿名函数详解

lambda x: float(x[1:-1]) 这个函数是啥意思,python 3.5

python 慕名函数