10

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()
4
  • see this question and my answer to this question Commented Sep 22, 2011 at 11:41
  • 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. Commented Sep 22, 2011 at 11:45
  • 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/… Commented Sep 22, 2011 at 13:15
  • 1
    Does this answer your question? What do (lambda) function closures capture? Commented May 27, 2021 at 15:54

5 Answers 5

12

you need to do:

lambda i=i: pprint(i)

instead to capture the current value of i

Sign up to request clarification or add additional context in comments.

Comments

3

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.

Comments

3

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.

Comments

0

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.

Comments

0

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.

3 Comments

It has nothing to do with the scope of the variable, it's the fact that what is closed is a reference to a value rather than a value. See my comment to the question -- if you move the for i in range loop into a function, so i doesn't exist in global scope, then call the function before the for k in li loop, it will give the same answer.
>if you move the for i in range loop into a function, so i doesn't exist in global scope, then call the function before the for k in li loop, it will give the same answer... --- i.e. non local? That's what i meant.
it is looking for variable i. as there is no function argument i it takes variable i from outer scope.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.