lambda forms within a loop

Chris Rebert clp2 at rebertia.com
Sun Oct 25 00:13:21 EDT 2009


On Sat, Oct 24, 2009 at 8:33 PM, Michal Ostrowski <mostrows at gmail.com> wrote:
> The snippet of code below uses two functions to dynamically create
> functions using lambda.
> Both of these uses should produce the same result, but they don't.
<snip>
> def MakeLambdaGood():
>  def DoLambda(x):
>     return lambda q: x + q
>  a = []
>  for x in [1,2]:
>     a.append(DoLambda(x))
>  return a
>
> def MakeLambdaBad():
>  a = []
>  for x in [1,2]:
>     a.append(lambda q:  x + q)
>  return a
>
> [a,b] = MakeLambdaBad()
> print a(10)
> print b(10)
> [a,b] = MakeLambdaGood()
> print a(10)
> print b(10)
<snip>
> The expected output of this code is
>
> 11
> 12
> 11
> 12
>
> However, what we get instead is
>
> 12
> 12
> 11
> 12
>
> The problem is that the two functions returned by MakeLambdaBad() are
> apparently the same, but the functions returned by MakeLambdaGood()
> are different.
>
> Can anyone explain why this would/should be the case?

This comes up often enough there should be a FAQ entry on it.

A decent explanation of the problem:
http://lackingrhoticity.blogspot.com/2009/04/python-variable-binding-semantics-part.html

Essentially, the lambdas in MakeLambdaBad() *don't* capture the value
of x at the time they are *created*; they both just reference the same
variable x in the function's scope and both use whatever its "current
value" at the time they get *called*. x's value gets frozen at 2 when
MakeLambdaBad() returns and the rest of the variables in x's scope are
destroyed; hence, they both use 2 as x's value.
The common workaround for the problem is to use the default argument
values mechanism in the lambdas.
MakeLambdaGood() avoids the problem by forcing x's evaluation (via
DoLambda()) at the time the lambdas are created (as opposed to called)
and thus reference x's value rather than x itself as a variable.

Cheers,
Chris
--
http://blog.rebertia.com



More information about the Python-list mailing list