学习目标
1、掌握函数作为返回值的使用。
函数作为返回值
高阶函数既可以接受函数作为参数,还可以把函数作为结果值返回。
实现一个可变参数的求和,通常的实现方法:
>>> def calc_sum(*args):
... ax = 0
... for n in args:
... ax = ax + n
... return ax
...
>>> calc_sum(1, 3, 5, 7)
16 #调用则立即返回求和结果
如果不需要立即求和,而是在后面的代码中根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
>>> def lazy_sum(*args):
... def sum():
... ax = 0
... for n in args:
... ax = ax + n
... return ax
... return sum
...
>>> f = lazy_sum(1, 3, 5, 7)
>>> f #不会立即返回求和结果,返回的是函数
<function lazy_sum.<locals>.sum at 0x001EC6F0>
>>> f()
16
当我们调用lazy_sum()函数时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7)
>>> f2 = lazy_sum(1, 3, 5, 7)
>>> f1 == f2
False
闭包
在上述例子中,函数lazy_sum()中定义了sum(),内部函数sum可以引用外部函数lazy_sum的参数和和局部变量,当lazy_sum返回sum时,相关参数和变量都保存在返回的函数中,这种叫做“闭包”。
返回函数引用循环变量:
>>> 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
返回的函数都引用了变量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))
... return fs
...
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
nonlocal
使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层函数的局部变量,返回的闭包函数调用正常:
>>> def inc():
... x = 0
... def fn():
... return x + 1 #引用外层函数的变量x
... return fn
...
>>> f = inc()
>>> f()
1
但是,如果对外层变量进行赋值,则Python会把x当作是fn()的局部变量:
>>> def inc():
... x = 0
... def fn():
... x = x + 1
... return x
... return fn
...
>>> f = inc()
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in fn
UnboundLocalError: local variable 'x' referenced before assignment
在内层函数fn()加nonlocal x声明可以解决该问题,解释器会把fn()的x看成是外层函数的局部变量:
>>> def inc():
... x = 0
... def fn():
... nonlocal x
... x = x + 1
... return x
... return fn
...
>>> f = inc()
>>> f()
1
>>> f()
2
>>> f()
3