函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。在以前的学习中,我们可能看见的求和函数都是像下面这样的:
def calcu(*args):
ax = 0
for n in args:
ax = ax + n
return ax
但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
>>> def calcu(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
>>> answer = calcu(1,2,4,6,7)
>>> answer
<function calcu.<locals>.sum at 0x0000022572CBC1E0>
我们可以看出当我们调用最大的calcu方法时,返回的是函数sum,而当我们再去调用sum函数时,返回的才是求得的和:
>>> answer()
20
在这个例子中,我们在函数calcu中又定义了函数sum,并且,内部函数sum可以引用外部函数calcu的参数和局部变量,当calcu返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为闭包的程序结构拥有极大的威力。
而且当我们调用calcu方法时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f = calcu(1,3,5,7,9)
>>> f
<function calcu.<locals>.sum at 0x000002257366C048>
>>> g = calcu(1,3,5,7,9)
>>> g
<function calcu.<locals>.sum at 0x000002257366C0D0>
>>> f == g
False
f 和 g 的调用互不影响。
闭包
注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
>>> def count():
fs = []
for i in range(1,4):
def f():
return i * i
fs.append(f)
return fs
你可能以为这段代码的结果是1,4,9,但是事实是这样的:
>>> f1,f2,f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
>>>def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
结果便如下面这样:
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
s = [0]
def counter():
s[0] = s[0]+1
return s[0]
return counter