python - 词法闭包是如何工作的?

标签 python closures lazy-evaluation late-binding python-closures

当我在调查 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”。

最佳答案

Python 实际上按照定义的方式运行。 创建了三个单独的函数,但它们每个都有定义它们的环境的闭包 - 在这种情况下,全局环境(或外部函数的环境,如果循环被放置在另一个函数中)。这正是问题所在 - 在这种环境中,i 被修改了,并且所有闭包都引用了相同的 i

这是我能想到的最佳解决方案 - 创建一个函数创建器并调用 that。这将为创建的每个函数强制不同的环境,每个函数中都有一个不同的 i

flist = []

for i in xrange(3):
    def funcC(j):
        def func(x): return x * j
        return func
    flist.append(funcC(i))

for f in flist:
    print f(2)

当您将副作用和函数式编程混合使用时会发生这种情况。

关于python - 词法闭包是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/233673/

相关文章:

haskell - 如何在常量内存中获取 make stats

python - 如何以特定方式将一个小矩阵沿对角线添加到一个较大的矩阵中?

python - Fudge:@patch 当 from X import Y'ing 而不是 import X 时不起作用?

javascript - Swift 中的闭包概念是否类似于 Javascript 中的闭包?

linux - 目录被过早删除

erlang - 为什么 Erlang 会在存在高阶函数的情况下放弃生成堆栈跟踪?

python - 如何在 django 中使用 South 将 CharField 替换为foreignkey?

Python/Pandas - 用于查看 DataFrame 或 Matrix 的 GUI

Jquery,从函数访问变量

Swift,如何传递多种类型的闭包?