tags:

views:

214

answers:

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
+23  A: 

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
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
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
See PEP 3104 for details: http://www.python.org/dev/peps/pep-3104/
Aaron Digulla
+1: Good, detailed reason for avoiding lambda.
S.Lott
+1 just for having fixed the bad question with actual and expected output.
Peter Hansen
+6  A: 

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
+4  A: 

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/

The MYYN
+3  A: 

This phenomena is called lambda binding see http://stackoverflow.com/questions/160859/what-is-lambda-binding-in-python

luc