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 中的行为不同的主要内容,如果未能解决你的问题,请参考以下文章