Python 2.7 函数是不是记住值而不是引用?关闭怪异
Posted
技术标签:
【中文标题】Python 2.7 函数是不是记住值而不是引用?关闭怪异【英文标题】:Have Python 2.7 functions remember value and not reference? Closure WeirdnessPython 2.7 函数是否记住值而不是引用?关闭怪异 【发布时间】:2011-10-07 21:41:19 【问题描述】:我试图从一个函数返回一个函数列表,每个函数都使用外部范围的变量。这是行不通的。这是一个演示正在发生的事情的示例:
a = []
for i in range(10):
a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2
为什么会发生这种情况,如何在 python 2.7 中解决它?
【问题讨论】:
【参考方案1】:i
每次都引用同一个变量,所以 i
在所有 lambda 表达式中都是 9,因为这是循环结束时 i
的值。最简单的解决方法是使用默认参数:
lambda x, i=i: x+i
这会将循环的 i
的值绑定到 lambda 定义时的局部变量 i
。
另一种解决方法是定义一个定义另一个 lambda 的 lambda,并调用第一个 lambda:
(lambda i: lambda x: x+i)(i)
如果您考虑这一点,这种行为会更有意义:
def outerfunc():
def innerfunc():
return x+i
a = []
for i in range(10):
a.append(innerfunc)
return a
这里,innerfunc
定义了一次,因此直观的感觉是您只使用一个函数对象,并且您不会期望循环创建十个不同的闭包。使用 lambda,它看起来 不像函数只定义一次,看起来你每次都在循环中重新定义它,但实际上它在功能上与 long 相同版本。
【讨论】:
嗯。真的没有办法让每个 lambda 保持自己的价值吗?我认为这就是结束的全部意义。 @Chironex,闭包在变量名而不是值上 所有声称具有闭包功能的编程语言都是这种情况吗? @Chironex - 这是他们的观点,但它不像你想象的那样工作! :) @phkahler:您正在为新定义的函数参数分配默认值。然后该值将存储在函数对象中,而不是使用词法范围规则。【参考方案2】:因为当您定义匿名函数(lambda 表达式)时,i 没有被评估,而是在它被调用时。您可以通过在a[1](1)
之前添加del i
来查看这一点:您将在a[1](1)
行上获得NameError: global name 'i' is not defined
。
你需要每次都将 i 的值固定到 lambda 表达式中,如下所示:
a = [lambda x, i=i: x+i for i in range(10)]
a[1](1) # returns 2
【讨论】:
【参考方案3】:另一种更通用的解决方案 - 也没有 lambda:
import operator
from functools import partial
a = []
for i in range(10):
a.append(partial(operator.add, i))
a[1][(1) # returns 2
这里的关键方面是functools.partial。
【讨论】:
partial(operator.add, i)
等价于(lambda i: lambda *args, **kwargs: operator.add(i, *args, **kwargs))()
但这绝对更漂亮,并且内置函数在可能的情况下比 lambda 表达式更好。我不会说这没有 lambdas:如果操作很复杂,它仍然会涉及某种类型的函数定义。
我认为最后一对括号中应该有一个i
。至于 lambdas:它至少没有 (double) lambdas 构造;) 而其中的 double 让我真的不寒而栗 :)
@agf:是的,5 分钟过去了 :)以上是关于Python 2.7 函数是不是记住值而不是引用?关闭怪异的主要内容,如果未能解决你的问题,请参考以下文章
在 Python 2 中,无论列表的内容如何,如何按值而不是引用复制复杂嵌套元素的列表
如果 Swift 通过值而不是引用传递,为啥我可以通过将 UIView 传递给函数来在 Swift 中操作 UIView 的属性? [复制]