Python-词汇闭包如何工作?

发布于 2021-02-02 23:21:34

当我研究Java代码中的词法闭包问题时,我在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”。

关注者
0
被浏览
182
1 个回答
  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

    实际上,Python的行为符合定义。创建了三个单独的函数,但是每个函数都封闭了定义它们的环境 -在这种情况下,是全局环境(如果将循环放在另一个函数内部,则为外部函数的环境)。不过,这确实是问题所在-在这种环境下,i发生了变异,并且所有闭包都引用相同的i。

    这是我能想到的最佳解决方案-创建一个函数创建器,然后调用它。这将为所创建的每个函数强制使用不同的环境,每个函数具有不同的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)
    

    当您混合副作用和功能编程时,就会发生这种情况。



  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

    循环中定义的函数会在i其值更改时继续访问相同的变量。在循环的最后,所有函数都指向同一个变量,该变量在循环中保存着最后一个值:结果就是示例中所报告的结果。

    为了评估i和使用其值,一种常见的模式是将其设置为参数默认值:在def执行语句时评估参数默认值,因此冻结了循环变量的值。

    预期的工作如下:

    flist = []
    
    for i in xrange(3):
        def func(x, i=i): # the *value* of i is copied in func() environment
            return x * i
        flist.append(func)
    
    for f in flist:
        print f(2)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看