10
votes

Code talks more:


from pprint import pprint

li = []

for i in range(5):
        li.append(lambda : pprint(i))

for k in li:
        k()

yield:

4
4
4
4
4

why not

0
1
2
3
4

??

Thanks.

P.S. If I write the complete decorator, it works as expected:



from pprint import pprint

li = []

#for i in range(5):
        #li.append(lambda : pprint(i))

def closure(i):
        def _func():
                pprint(i)
        return _func

for i in range(5):
        li.append(closure(i))

for k in li:
        k()
5
You can see that is is closing the variable by moving the original loop into a function, and then calling the function before the for k in li: line, so that i isn't a valid name in the module level scope. It will still work (and get the same result because it's closing a reference not the value), meaning that the name is closed. - agf
Thank you all. I think stackoverflow.com/questions/2295290/… tells not only the solution but also why. I think my question is duplicate with stackoverflow.com/questions/2295290/… - Grissiom
Does this answer your question? What do (lambda) function closures capture? - ggorlen

5 Answers

12
votes

you need to do:

lambda i=i: pprint(i)

instead to capture the current value of i

3
votes

It does properly reference i, only the thing is, that by the time your list gets populated, i would have the value assigned form the last item in the sequence when the iteration ended, so that's why your seeing 4 all over.

3
votes

If you don't want to use a default argument -- which could introduce bugs if accidentally called with an argument -- you can use a nested lambda:

from pprint import pprint

li = []

for i in range(5):
    li.append((lambda x: lambda: pprint(x))(i))

for k in li:
    k()

This is an anonymous version of your closure function.

0
votes

The lambda functions create closures over the i variable. After the first for loop is finished, i has value 4.

Then the second for loop starts and all the lambda functions are executed. Each of them then prints the current value of i, which is 4.

0
votes

The answer: Because the code inside the lambda is using your global variable i.

Your second variant will do the same as the first one with lambda if you remove the parameter i:

def closure():

Instead of

def closure(i):

I.e. the code inside the function will use the global variable i.