7

下面是我从某人的博客中获得的关于 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 有没有人可以帮忙解释一下?
先感谢您。

4

3 回答 3

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))
于 2012-07-10T07:33:45.580 回答
4

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

于 2012-07-10T07:31:32.290 回答
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))
于 2012-07-10T07:35:41.810 回答