以下是我从某人关于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
有没有人能解释一下呢?
提前感谢您。
在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))
您没有创建闭包。您正在生成一组每个都访问全局变量 i
的函数,第一次循环后它等于2。因此,每次函数调用都会得到 2 * 2。
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))
lambda
的选项,因为它们对于一些小事情确实有意义。虽然现在我很少需要它们;在2.7中,生成器/字典/集合推导式已经消耗了它们很多的用处。 - Walter Mundt