While I was investigating a problem I had with lexical closures in Javascript code, I came along this problem in Python:
flist = []
for i in xrange(3):
def func(x): return x * i
flist.append(func)
for f in flist:
print f(2)
Note that this example mindfully avoids lambda
. It prints "4 4 4", which is surprising. I'd expect "0 2 4".
This equivalent Perl code does it right:
my @flist = ();
foreach my $i (0 .. 2)
{
push(@flist, sub {$i * $_[0]});
}
foreach my $f (@flist)
{
print $f->(2), "\n";
}
"0 2 4" is printed.
Can you please explain the difference ?
Update:
The problem is not with i
being global. This displays the same behavior:
flist = []
def outer():
for i in xrange(3):
def inner(x): return x * i
flist.append(inner)
outer()
#~ print i # commented because it causes an error
for f in flist:
print f(2)
As the commented line shows, i
is unknown at that point. Still, it prints "4 4 4".
The functions defined in the loop keep accessing the same variable
i
while its value changes. At the end of the loop, all the functions point to the same variable, which is holding the last value in the loop: the effect is what reported in the example.In order to evaluate
i
and use its value, a common pattern is to set it as a parameter default: parameter defaults are evaluated when thedef
statement is executed, and thus the value of the loop variable is frozen.The following works as expected:
The reasoning behind the behavior has already been explained, and multiple solutions have been posted, but I think this is the most pythonic (remember, everything in Python is an object!):
Claudiu's answer is pretty good, using a function generator, but piro's answer is a hack, to be honest, as it's making i into a "hidden" argument with a default value (it'll work fine, but it's not "pythonic").
The problem is that all of the local functions bind to the same environment and thus to the same
i
variable. The solution (workaround) is to create separate environments (stack frames) for each function (or lambda):Python is actually behaving as defined. Three separate functions are created, but they each have the closure of the environment they're defined in - in this case, the global environment (or the outer function's environment if the loop is placed inside another function). This is exactly the problem, though - in this environment, i is mutated, and the closures all refer to the same i.
Here is the best solution I can come up with - create a function creater and invoke that instead. This will force different environments for each of the functions created, with a different i in each one.
This is what happens when you mix side effects and functional programming.
I'm still not entirely convinced why in some languages this works one way, and in some another way. In Common Lisp it's like Python:
Prints "6 6 6" (note that here the list is from 1 to 3, and built in reverse"). While in Scheme it works like in Perl:
Prints "6 4 2"
And as I've mentioned already, Javascript is in the Python/CL camp. It appears there is an implementation decision here, which different languages approach in distinct ways. I would love to understand what is the decision, exactly.
The variable
i
is a global, whose value is 2 at each time the functionf
is called.I would be inclined to implement the behavior you're after as follows:
Response to your update: It's not the globalness of
i
per se which is causing this behavior, it's the fact that it's a variable from an enclosing scope which has a fixed value over the times when f is called. In your second example, the value ofi
is taken from the scope of thekkk
function, and nothing is changing that when you call the functions onflist
.