开发者

Why does the following code always output 16? [duplicate]

This question already has answers here: Creating functions (or lambdas) in a loop (or comprehension) (6 answers) Closed 4 months ago.
def makeActions(开发者_如何学JAVA):
    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


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.


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


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/


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜