2

I have an array of functions, for example:

>>> def f():
...     print "f"
... 
>>> def g():
...     print "g"
... 
>>> c=[f,g]

Then i try to create two lambda functions:

>>> i=0
>>> x=lambda: c[i]()
>>> i+=1
>>> y=lambda: c[i]()

And then, call them:

>>> x()
g
>>> y()
g

Why c[i] in lambda are the same?

EgorPuzyrev
  • 119
  • 1
  • 8
  • The _easy_ way to solve this problem is to just not create the useless `lambda`s in the first place. Just replace those two lines with `x = c[i]` and `y = c[i]`, and you will get exactly the functions you wanted. The only reason to ever write `lambda: f()` instead of `f` is to stick `f` into a closure namespace to look it up later, instead of just using it. You don't want to do that here, and in fact that's exactly what's causing your problem. – abarnert May 26 '13 at 23:15

2 Answers2

10

That's because the lambda function is fetching the value of the global variable i at runtime:

>>> i = 0
>>> x=lambda z = i : c[z]() #assign the current value of `i` to a local variable inside lambda
>>> i+=1
>>> y =lambda z = i : c[z]()
>>> x()
f
>>> y()
g

A must read: What do (lambda) function closures capture?

Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • Note: the accepted answer for the linked question shows how to make the desired name capture happen (see the `createAdder` function at the end of http://stackoverflow.com/a/2295368/25050. – Mr Fooz May 26 '13 at 21:08
3

In Python closures don't capture actual values, but instead they capture namespaces. So when you use i inside your function it's actually looked up in the enclosing scope. And the value there has already changed.

You don't need all those lambdas and lists to see this.

>>> x = 1
>>> def f():
...   print(x)
...
>>> x = 2
>>> def g():
...   print(x)
...
>>> g()
2
>>> f()
2
kirelagin
  • 13,248
  • 2
  • 42
  • 57