views:

106

answers:

1

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?

+12  A: 

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): -- 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 do f = make_f(kwargs, k) instead of the def statement.

Alex Martelli
Thanks a lot, very informative and quick response.
sharvey