当我调查我在 Javascript 代码中遇到的词法闭包问题时,我在 Python 中遇到了这个问题:
flist = []
for i in xrange(3):
def func(x): return x * i
flist.append(func)
for f in flist:
print f(2)
请注意,此示例有意识地避免了 lambda
。它打印出“4 4 4”,这是令人惊讶的。我期待“0 2 4”。
这个等效的 Perl 代码是正确的:
my @flist = ();
foreach my $i (0 .. 2)
{
push(@flist, sub {$i * $_[0]});
}
foreach my $f (@flist)
{
print $f->(2), "\n";
}
打印“0 2 4”。
你能解释一下区别吗?
更新:
问题 不在于 i
是全局的。这显示相同的行为:
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)
正如注释行所示, i
在这一点上是未知的。尽管如此,它仍会打印“4 4 4”。
原文由 Eli Bendersky 发布,翻译遵循 CC BY-SA 4.0 许可协议
Python 实际上按照定义运行。创建了 三个独立的函数,但它们每个都有 定义它们的环境的闭包——在本例中为全局环境(如果循环位于另一个函数内,则为外部函数的环境)。不过,这正是问题所在——在这种环境中, i 被修改 了,闭包都 指向同一个 i 。
这是我能想到的最佳解决方案——创建一个函数创建器并调用 _它_。这将为创建的每个函数强制使用 不同的环境,每个函数中都有 不同的 i 。
当您混合使用副作用和函数式编程时,就会发生这种情况。