0

Consider the following code:

def A():
    l = list()
    for i in range(5):
        l.append(lambda: i)
    return l

for f in A():
    print( f() )

It prints out 4 five times, and I'm assuming it does that because the lambda function has taken the variable i through the outer scope of A, but right before the moment the variable became out of scope.

Then we have this code:

def B():
    l = list()
    for i in range(5):
        l.append((lambda i: lambda: i)(i))
    return l

for f in B():
    print( f() )

It prints out all numbers from 0 to 4 in order. I am assuming it does that because the variable i has been grabbed from the scope of the outer lambda which took it as a parameter, so the value has been assigned to the cell in the moment when that external lambda finished executing.

But what if i held a mutable object instead of an immutable int?

def C():
    l, i = list(), list()
    for r in range(5):
        i.append(r)
        l.append((lambda i: lambda: i)(i.copy()))
    return l

for f in C():
    print( f() )

It does print out the lists in order as expected, because I used the list.copy() method in the argument.

Now, is my understanding correct?

If so, then is there a more pythonic or simpler way to save in a closure cell either an immutable object (or a copy of a mutable object), at the exact moment I want to? In other words, is there a better way than the (lambda i: lambda: i)(i) latch I came up with?

GT 77
  • 448
  • 5
  • 12
  • 1
    Does this answer your question? [Python lambda's binding to local values](https://stackoverflow.com/questions/10452770/python-lambdas-binding-to-local-values) – juuso Jan 29 '22 at 11:47
  • 1
    Closures capture variables, not values. A closure doesn't save the state of a variable at some particular time; it saves the variable itself. – user2357112 Jan 29 '22 at 12:09

1 Answers1

1

This happens because of late binding, try this code:

def bind(value):
  return lambda: value

def A():
    l = list()
    for i in range(5):
        l.append(bind(i))
    return l

for f in A():
    print( f() )

This way you can obtain the expected behaviour, since the binding is now on a value and not on a variable. Of course this is something tricky, and you should pay attemption to the behaviours of function like this, since python is using late binding.

juuso
  • 612
  • 7
  • 26