4
def makeActions():
    acts=[]
    for i in range(5):
        print len(acts)
        acts.append(lambda x: i ** x)
        print acts[i]
    return acts
acts=makeActions()
for i in range(5):
    print acts[i](2)

Output:

16
16
16
16
16

Expected output:

0
1
4
9
16
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
user198729
  • 61,774
  • 108
  • 250
  • 348

4 Answers4

22

Because the i in the lambda is probably not what you expect. To verify this, change the code:

acts.append(lambda x: (i, i ** x))

Now the print tells you the value of i:

(4, 16)
(4, 16)
(4, 16)
(4, 16)
(4, 16)

This means that the lambda doesn't copy the value of i but keeps a reference to the variable, so all lambdas see the same value. To fix this, copy i:

acts.append(lambda x, i=i: (i, i ** x))

The little i=i creates a local copy of i inside the lambda.

[EDIT] Now why is this? In the versions of Python before 2.1, local functions (i.e. functions defined inside of other functions) couldn't see the variables in the same scope.

def makeActions():
    acts=[]
    for i in range(5):
        print len(acts)
        def f(x):   # <-- Define local function
            return i ** x
        acts.append(f)
        print acts[i]
    return acts

then you'd get an error that i isn't defined. lambda could see the enclosing scope at the cost of a somewhat wierd syntax.

This behavior has been fixed in one of the recent versions of Python (2.5, IIRC). With these old versions of Python, you'd have to write:

        def f(x, i=i):   # <-- Must copy i
            return i ** x

Since the fix (see PEP 3104), f() can see variables in the same scope, so lambda isn't necessary anymore.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Whoa. So why i in the first example is always 4? Isnt it natural to make local copy when lambda is created? Now its making copy as well but in strange place. – qba Dec 22 '09 at 10:11
  • Because the first lambda gets a reference to the loop variable `i`. In fact the `i` in the lambda code is the very same `i` in the `for`. As the loop variable changes, all the references to it change as well. When you say `i=i` in a function/lambda definition, then you say: Create a new parameter and initialize it with the current value of the loop variable `i`. – Aaron Digulla Dec 22 '09 at 10:34
  • +1 just for having fixed the bad question with actual and expected output. – Peter Hansen Dec 22 '09 at 13:53
6

Because all lambda functions you create are bound to i, which becomes 4 at the end of loop, and as we all well know 4*4 = 16

to avoid that create your functions using nested function(closure) e.g.

def makePowerFunc(base):
    def powerFunc(x):
        return base**x
    return powerFunc

def makeActions():
    acts=[]
    for i in range(5):
        acts.append(makePowerFunc(i))

    return acts
acts=makeActions()
for i in range(5):
print acts[i](2)

output:

0
1
4
9
16

There are other ways to solve it, but it is better to have a named nested function instead of lambda, and you can do many more things with such closures

Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
5

It's counterintuitive or at least less common syntax. I guess you meant:

acts.append(lambda x, i = i: i ** x)

which will output:

0
1
4
9
16

fn. in your version,

acts.append(lambda x, i: i ** x)

the lambda functions were created, but they all referenced the local i from the loop, which stopped at i = 4, so all your lambdas were saying: lambda x: 4 ** x, hence

for i in range(5):
    print acts[i](2)

would print all 16s.


ffn. a blog post about broken lambda: http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/

miku
  • 181,842
  • 47
  • 306
  • 310
3

This phenomena is called lambda binding see What is "lambda binding" in Python?

Community
  • 1
  • 1
luc
  • 41,928
  • 25
  • 127
  • 172