Why does the following code always output 16? [duplicate]
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 lambda
s 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?
精彩评论