0

Not sure if this is called a generator, a factory or something else, but it's not doing what I would expect.

d = {}

for i in range(10):
    def func():
        print i
    d[i]=func

print d

for f in d:
    d[f]()

This outputs:

9
9
9
9
9
9
9
9
9
9

But I was expecting:

0
1
2
3
4
5
6
7
8
9

I want to store the current state of func at each step of the for loop, but it seems that the dictionary is getting a reference(?) to the function. How do I stop this?

Also, if it is storing a reference to the function, I would epect this to print out 'overridden', but it still prints all 9s. Why is that?

d = {}

for i in range(10):
    def func():
        print i
    d[i]=func

print d

def func():
    print 'overridden'

for f in d:
d[f]()

Thanks!

phimath
  • 1,322
  • 2
  • 12
  • 22
  • 1
    The usual solution is to force the capture with a default value like in [this answer](http://stackoverflow.com/a/2295372/5014455) – juanpa.arrivillaga Oct 15 '16 at 03:51
  • I am having trouble understanding how to apply that. I have tried adding `x = i` in the function but it is the same result. Can you explain what this does? `lambda a,i=i: i+a` I have only seen lambda like `lambda x: x+1` – phimath Oct 15 '16 at 03:55
  • 1
    It uses a default value. The only line you need to change is `def func():` to `def func(i=i):` – juanpa.arrivillaga Oct 15 '16 at 03:58
  • Wow, yeah that worked! Thank you. Why does this work though? I now understand that using a default parameter forces it to capture the current value, but is there a reason why? Is this behavior used elsewhere in python or is this just a one off way to do this? – phimath Oct 15 '16 at 04:01
  • 1
    Because default values are bound at function definition time. It's the reason [default mutable arguments bite newbies](http://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument). Since closures are lexically scoped in Python, `i` refers to whatever the name `i` refers to *in the scope that `i` has when it was defined*. Using a default value is a trick to force the scope to be local. So, just for fun try `i = 'hey'` and then `d[f]()` when you don't use the default value. – juanpa.arrivillaga Oct 15 '16 at 04:07
  • That is quite a bit to digest. Thank you! – phimath Oct 15 '16 at 04:18

0 Answers0