关于python闭包

时间:2012-07-10 07:28:04

标签: python closures

以下是我从某人关于python关闭的博客中得到的一个例子。 我在python 2.7中运行它并获得与我期望不同的输出。

flist = []

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

for f in flist:
    print f(2)

我的预期输出为:0,2,4 但输出是:4,4,4 有没有人可以帮忙解释一下? 提前谢谢。

3 个答案:

答案 0 :(得分:17)

循环不会在Python中引入范围,因此所有三个函数都关闭相同的i变量,并在循环结束后引用其最终值,即2。

似乎几乎每个与我交谈的人都使用Python中的闭包。推论是外部函数可以改变i但是内部函数不能(因为这会使i成为局部而不是基于Python语法规则的闭包。)

有两种方法可以解决这个问题:

# avoid closures and use default args which copy on function definition
for i in xrange(3):
    def func(x, i=i):
        return x*i
    flist.append(func)

# or introduce an extra scope to close the value you want to keep around:
for i in xrange(3):
    def makefunc(i):
        def func(x):
            return x*i
        return func
    flist.append(makefunc(i))

# the second can be simplified to use a single makefunc():
def makefunc(i):
    def func(x):
        return x*i
    return func
for i in xrange(3):
    flist.append(makefunc(i))

# if your inner function is simple enough, lambda works as well for either option:
for i in xrange(3):
    flist.append(lambda x, i=i: x*i)

def makefunc(i):
    return lambda x: x*i
for i in xrange(3):
    flist.append(makefunc(i))

答案 1 :(得分:4)

您不是在创建闭包。您正在生成一个函数列表,每个函数访问全局变量i,该函数在第一个循环后等于2。因此,每次函数调用最终都会得到2 * 2.

答案 2 :(得分:1)

每个函数都访问全局i

functools.partial来救援:

from functools import partial
flist = []

for i in xrange(3):
    def func(x, multiplier=None):
        return x * multiplier
    flist.append(partial(func, multiplier=i))