我有一些函数元组,我想用一些数据预先加载。目前,我这样做的方式如下。本质上,我列出了新函数,并一次一个地添加lambda函数,然后重新转换为元组。但是,当我在代码的不同部分使用这些函数时,每个函数都表现为列表中的最后一个函数。
def newfuncs(data, funcs):
newfuncs = []
for f in funcs:
newf = lambda x: f(x, data)
newfuncs.append(newf)
return tuple(newfuncs)
以下是问题的一个简单示例
funcs = (lambda x, y: x + y, lambda a, b: a - b)
funcs = newfuncs(10, funcs)
print(funcs[0](5))
print(funcs[1](5))
我希望打印数字15,然后是-5。但是,此代码打印数字-5两次。如果有人能帮助我理解为什么会这样,那将非常感激。谢谢!
答案 0 :(得分:3)
如上所述,问题在于变量f
,它是分配给所有 lambda
函数的变量,因此在循环结束时,每个 lambda
都会看到相同的f
。
此处的解决方案是使用functools.partial
,或为lambda
创建作用域默认参数:
def newfuncs(data, funcs):
newfuncs = []
for f in funcs:
newf = lambda x, f=f: f(x, data) # note the f=f bit here
newfuncs.append(newf)
return tuple(newfuncs)
现在像以前一样调用这些lambda
给出:
15
-5
如果您正在使用python3.x,请务必查看此comment by ShadowRanger作为作用域默认arg方法的可能安全功能。
答案 1 :(得分:2)
这是一个众所周知的Python“问题”,或者我应该说“这只是Python的方式。”
您创建了元组:
( x => f(x, data), x => f(x, data) )
但是f
是什么?在最终调用函数之前,不会对f
进行求值!
首先,f
为(x, y)=>x+y
。然后在for
循环中, f
被重新分配到 (x, y)=>x-y
。
当你最终调用你的函数时,那么,只有这样,才会查找f
的值。此时f
的价值是多少?所有功能的值均为(x, y)=>x-y
。你的所有函数都做减法。这是因为f
被重新分配。只有一个f
。并且在调用任何lambda之前,将该值和f
的值设置为减法函数。
<强>附录强>
如果有人感兴趣,不同的语言会以不同的方式解决这个问题。 JavaScript在这里相当有趣(有些人会说是混乱),因为它以Python的方式执行,OP发现意外,以及OP预期的不同方式。在JavaScript中:
> let funcs = []
> for (let f of [(x,y)=>x+y, (x,y)=>x-y]) {
funcs.push(x=>f(x,10));
}
> funcs[0](5)
15
funcs[1](5)
-5
但是,如果您将let
更改为上面的var
,它的行为就像Python一样,两者都得到-5!这是因为使用let
,for循环的每次迭代都会得到不同的f
;使用var
,整个函数共享相同的f
,这将继续重新分配。这就是Python的工作原理。
cᴏʟᴅsᴘᴇᴇᴅ向你展示了你所期望的方法是确保你为每次迭代得到不同的f
,Python允许你以一种非常简洁的方式做到,默认lambda的第二个参数到该迭代的本地f
。这很酷,所以如果对你有帮助,他们的答案应该被接受。