在循环中对Python中的函数进行柯里化

3 投票
2 回答
2661 浏览
提问于 2025-04-16 00:22

这里有一些代码,简化了我正在做的事情:

vars = {
    'a':'alice',
    'b':'bob',
}
cnames = ['charlie', 'cindy']

commands = []

for c in cnames:
    kwargs = dict(vars)
    kwargs['c'] = c
    print kwargs
    commands.append(lambda:a_function(**kwargs))

print commands

def a_function(a=None, b=None, c=None):
    print a
    print b
    print c

for c in commands:
    print "run for "+ repr(c)
    c()

这是它的输出结果:

{'a': 'alice', 'c': 'charlie', 'b': 'bob'}
{'a': 'alice', 'c': 'cindy', 'b': 'bob'}
[<function <lambda> at 0x1001e9a28>, <function <lambda> at 0x1001e9e60>]
run for <function <lambda> at 0x1001e9a28>
alice
bob
cindy
run for <function <lambda> at 0x1001e9e60>
alice
bob
cindy

我本来期待看到charlie,然后是cindy,为什么cindy会显示两次呢?

2 个回答

5

你遇到了一个经典的绑定时间问题,而@Mike的解决方案也是经典的。一个不错的替代方法是写一个高阶函数:

def makecall(kwargs):
  def callit():
    return a_function(**kwargs)
  return callit

然后在你的循环中使用 commands.append(makecall(kwargs))。这两种解决方案的原理是一样的(通过传递一个参数来强制提前绑定——在我的例子中是一个普通参数,而在@Mike的例子中是一个命名参数的默认值);选择哪种方法只是风格或优雅的问题(我个人来说,虽然在非常简单的情况下可以接受 lambda,但只要稍微复杂一点,我就更喜欢传统的 def 方法;-)。

4

一个函数的内容在你调用这个函数之前是不会执行的。当你写 lambda: a_function(**kwargs) 的时候,kwargs 这个东西不会被查找,直到你真正去 调用 这个函数。到那时,它的值会被设置为你在循环中最后一次创建的那个。

一个能得到你想要结果的解决办法是这样写:commands.append(lambda kwargs=kwargs: a_function(**kwargs))

撰写回答