开发者

currying functions in python in a loop

So here is some code that simpl开发者_运维技巧ifies what I've been working on:

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()

And here is its output:

{'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

I would expect to get charlie, then cindy, why is cindy being displayed twice?


You're encountering a classic binding-time problem, and @Mike's solution is the classic one. A good alternative is to write a higher order function:

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

and use commands.append(makecall(kwargs)) in your loop. Both solutions work on the same principle (by forcing early binding through passage of an argument -- a plain argument in my case, a default value for a named argument in @Mike's); the choice is just a matter of style or elegance (me, while I tolerate lambda in super-simple cases, as long as the subtlest complication intervenes I vastly prefer good old def;-).


A function's body isn't ran until the function is called. When you do lambda: a_function(**kwargs), kwargs isn't looked up until you actually call the function. At that point it's assigned to the last one you made in the loop.

One solution that gets the result you want would be to do commands.append(lambda kwargs=kwargs: a_function(**kwargs))

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜