返回函数顾名思义就是将函数作为返回值返回。一般情况下的求和函数是这样事儿的:
>>> def calc_sum(*args): ax = 0 for n in args: ax = ax +n return ax >>> calc_sum(1,2,3,4,5) 15
但是有些情况下,我们不希望程序立即计算求和,而是需要时再计算,可以让函数不返回结果,而是返回一个函数:
>>> def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum >>> f = lazy_sum(1,2,3,4,5) >>> f .sum at 0x10d696280> >>> f() 15
可以看到,直接调用lazy_sum时,并没有进行计算,而是返回了一个sum函数,只有调用f()时,才会计算真正的结果。
我们知道这里在lazy_sum中定义了一个sum函数,sum函数成为内部函数,lazy_sum成为外部函数,内部函数可以引用外部函数的参数和变量,而当外部函数lazy_sum返回sum时,相关的参数和变量都保存到了返回的函数中,这种程序称之为闭包(“Closure”)。
值得注意的一点,调用lazy_sum时,每次调用返回的都是一个全新的函数,即使传入参数一致:
>>> f1 = lazy_sum(1,2,3,4,5) >>> f2 = lazy_sum(1,2,3,4,5) >>> f1 == f2 False
关于闭包
上面的例子中返回的函数在内部引用了局部变量args,当一个函数返回了新的函数,它的内部变量还会被新的函数引用。下面看一个关于循环的例子:
>>> def count(): fs = [] for i in range(1,4): def f(): return i*i fs.append(f) return fs >>> f1,f2,f3 = count() >>> f1() 9 >>> f2() 9 >>> f3() 9
为什么f1,f2,f3没有按我们预想的一样输出1,4,9呢?因为返回的函数引用了变量i,在for循环结束时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)) return fs >>> f1,f2,f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9
下面,利用闭包返回一个计数器函数,每次调用它返回递增整数:
>>> def createcounter(): n = 0 # 先定义一个变量作为初始值 def counter(): # 定义一个内部函数用来计数 nonlocal n # 声明变量n非内部函数的局部变量,否则内部函数只能引用,一旦修改会视其为局部变量,报错“局部变量在赋值之前被引用” n += 1 # 每调用一次内部函数,对n + 1 return n # 返回n的值 return counter >>> counterA = createcounter() >>> print(counterA(), counterA(), counterA(), counterA(), counterA()) 1 2 3 4 5