Creating functions in a loop

2018-12-31 07:08发布

I'm trying to create functions inside of a loop and storing them in a dictionary. The problem is that all entries in the dictionary seem end up mapping to the last created function. The code goes like this:

d = {}
def test(**kwargs):
    for k in kwargs:
        def f():
            print(k, kwargs[k])
        d[k] = f
        f()

test(foo=1, bar=2)
print('should print the same output as before')
d['foo']()
d['bar']()

This outputs:

foo 1
bar 2
should print the same output as before
bar 2
bar 2

Any idea why?

1条回答
像晚风撩人
2楼-- · 2018-12-31 07:56

You're running into a problem with late binding -- each function looks up k as late as possible (thus, when called outside test, this happens after the end of the loop).

Easily fixed by forcing early binding: change def f(): to def f(k=k): like this:

def f(k=k):
    print(k, kwargs[k])

Default values (the right-hand k in k=k is a default value for argument name k, which is the left-hand k in k=k) are looked up at def time, not at call time, so essentially they're a way to specifically looking for early binding.

If you're worried about f getting an extra argument (and thus potentially being called erroneously), there's a more sophisticated way which involved using a closure as a "function factory":

def make_f(kwargs, k):
    def f():
        print(k, kwargs[k])
    return f

and in your loop use f = make_f(kwargs, k) instead of the def statement.

查看更多
登录 后发表回答