我想创建几个类似的lambda函数。
当我使用简单的循环时,我得不到我期望的结果。
代码:
funcList = []
for i in range(2):
f = lambda x: i*x
print("Before: ", f(10))
funcList.append(f)
print("After: ", funcList[0](10), funcList[1](10))
输出:
Before: 0
Before: 10
After: 10 10
代码:
funcList = [lambda x: i*x for i in range(2)]
print("After: ", funcList[0](10), funcList[1](10))
输出:
After: 10 10
如何创建几个lambda函数,使用" original" i
的值,而不是i
的最后已知值?
答案 0 :(得分:1)
问题如下:Python中的闭包(请参阅lambdas vs closures)关闭其环境,将变量名称映射到对象,并且他们通过将封闭的环境添加到其作用域查找链来实现此目的。考虑一个更简单,更明确的例子:
import sys
def foo():
result = []
for i in range(2):
def bar():
return i
result.append(bar)
return result
foo
的范围查找链,在它返回之前具有环境,如下所示:
- "foo" local variables: {i: 1, result: [bar-function-1, bar-function-2], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}
也就是说,如果foo
尝试使用变量z
,则首先查看第一个环境(foo
的局部变量)。如果它在那里,查找成功。如果没有,它将移动到下一个(全局变量)。如果它在那里,查找成功。如果没有,那么链上就没有其他环境了,所以你得到NameError
。
在这种情况下,result
和i
会被找到为局部变量foo
和sys
,也许其他人会被发现为全局变量,而其他所有变量都会被找到给NameError
。
每个bar
中的范围查找链类似于:
- "bar" local variables: {}
- "foo" local variables: {i: 1, result: [bar-function-1, bar-function-2], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}
最重要的是,foo
的本地变量环境未被复制到bar
的本地变量环境中。因此,bar
可以查找i
,但它首先在bar
的局部变量中找不到它,然后在范围链中找到它并在其中找到它foo
的局部变量。因此,当定义第一个bar
函数时,其范围查找链如下所示:
- "bar" local variables: {}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}
但是,当foo
更改其本地变量i
时,bar的范围查找链现在看起来像这样:
- "bar" local variables: {}
- "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}
所以现在当bar
查找i
它再次无法在其局部变量中找到它时,查找范围查找链中的一个,并找到foo
' s局部变量i
...现在为1,因为它与之前的i
相同。
我在评论中写的伎俩有点像黑客。为了更明确一点,请考虑:
def foo():
result = []
for i in range(2):
def bar(j=i):
return j
result.append(bar)
return result
真正发生的事情是您使用参数bar
声明j
,其默认值设置为{{值 i
1}}(即i
在定义时引用的对象 ...而不是i
随时引用的对象{{1} }在j
中使用。因此,第一个bar
函数的范围查找链如下所示:
bar
当循环再次出现时,它看起来像这样:
- "bar" local variables: {j: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}
在这两种情况下,- "bar" local variables: {j: 0}
- "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}
的查找会立即在j
的本地变量中成功,而bar
永远不会发生变化。
执行以下操作有点骇人听闻,因为它隐藏了外部j
并使其看起来像是指同一i
:
i
但实际上,它们是两个不同的def foo():
result = []
for i in range(2):
def bar(i=i):
return i
result.append(bar)
return result
:
i
并且,在循环的第二次迭代中:
- "bar" local variables: {i: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}
可能更多"适当"方法是做类似的事情:
- "bar" local variables: {i: 0}
- "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}
在这种情况下,范围链是:
def make_bar(j):
def bar():
return j
return bar
def foo():
result = []
for i in range(2):
bar = make_bar(i)
result.append(bar)
return result
并且,在循环的第二次迭代中:
- "bar" local variables: {}
- "make_bar" local variables: {j: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {make_bar: make_bar-function, foo: foo-function, sys: sys-module, etc...}
在这种情况下,它有效,因为- "bar" local variables: {}
- "make_bar" local variables: {j: 0}
- "foo" local variables: {i: 1, result: [], bar: bar-function-2}
- global variables: {make_bar: make_bar-function, foo: foo-function, sys: sys-module, etc...}
被make_bar
调用,i
被称为将其局部变量make_bar
设置为对象{{1} }指在通话时(即j
)。当i
0
发生变化时,make_bar
的本地变量j
不会发生变化。
要在您的案例中做一个完全明确的例子,您可以执行以下操作:
foo
i
或者只是:
funcList = []
for i in range(2):
def make_f(j):
def f(x):
return j * x
funcList.append(make_f(i))
或者你可以使用我提出的黑客,现在知道其工作原理的完整故事:
funcList = []
for i in range(2):
funcList.append((lambda j: lambda x: j*x)(i))
答案 1 :(得分:0)
这是由于后期绑定。
funcList[0](10)
时,它将返回i * x funcList[0](10)
的所有调用都将返回10 答案已在评论f = lambda x,i=i: i*x
中给出,它将避免后期绑定,i的值将在创建函数对象时得到解决。