在我调查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”,这是令人惊讶的。我希望是“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
在这一点上是未知的。尽管如此,它还是打印了“444”。
下面是如何使用
functools
库(我不确定在提出问题时是否可用)来实现这一点。如预期,输出0 2 4。
循环中定义的函数在其值更改时继续访问同一变量
i
。在循环结束时,所有函数都指向同一个变量,该变量保存循环中的最后一个值:示例中报告的效果。为了计算
i
并使用其值,一种常见的模式是将其设置为参数默认值:当执行def
语句时计算参数默认值,从而冻结循环变量的值。以下工作符合预期:
Python的行为实际上是按定义的。创建了三个独立的函数,但每个函数都具有在中定义的环境的闭包—在本例中,是全局环境(如果循环放在另一个函数中,则是外部函数的环境)。不过,这正是问题所在,在这个环境中,i发生了变异,所有的闭包都指向同一个i。
这是我能想到的最好的解决方案-创建一个函数creater并调用来代替它。这将为每个创建的函数强制不同的环境,每个函数中有一个不同的i。
当你把副作用和函数式编程混合在一起时,就会发生这种情况。
相关问题 更多 >
编程相关推荐