函数作为返回值
高阶函数除了可以接收函数作为参数外,还可以把函数作为结果值返回。
我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:
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) print f # <function sum at 0x02657770> # lazy_sum(1,2,3,4,5)返回的是一个指向求和的函数的函数名。 # 在调用lazy_sum(1,2,3,4,5)的时候,不立刻求和,而是根据后面代码的需要在计算。 print f() # 15 # 用f()调用求和函数,计算出结果。 f1 = lazy_sum(1,2,3,4,5,6) f2 = lazy_sum(1,2,3,4,5,6) print f1 == f2 # False # lazy_sum()每调用一次,都会返回一个独一无二的函数地址。
例中,lazy_sum中的内部函数sum引用了外部函数lazy_sum的参数和局部变量,
当lazy_sum返回函数sum时,相关参数和变量已经保存在返回的函数sum中了。
我们称这为 闭包。
注意到返回的函数在其定义内部引用了局部变量args
,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()
才执行。我们来看一个例子:
def count(): fs = [] for i in range(1,4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() print f1() print f2() print f3() # 9 # 9 # 9
结果全部都是9. 不是预期的1,4,9!
遂在编辑器中不断更改并调试运行观察变量的变化过程:
# -*- coding: utf-8 -*- def count(): fs = [] for i in range(1,4): def f(): return i*i fs.append(f) return fs # f1, f2, f3 = count() #将上述代码分开写: f1 = count() f2 = count() f3 = count() print(f1()) print(f2()) print(f3())
结果编译器报错:
Traceback (most recent call last):
File "***.py", line 16, in <module>
print(f1())
TypeError: 'list' object is not callable
调试后发现原因:
f1 = count()这条语句执行时,count()函数会先执行,并将结果fs(这是一个包含三个元素(值是三个不同函数)的list!)返还f1。所以f1()是将list当函数用,故报错!
f2 = count()和f3 = count()也是这个道理。
所以第一个要弄清楚的是 :初始代码中的f1, f2, f3 = count()是将count() 中返回值(list)的三个元素(元素值是函数)分别赋值给f1,f2,f3!此时它们是函数!
修改代码:
# -*- coding: utf-8 -*- def count(): fs = [] for i in range(1,4): def f(): return i*i fs.append(f) return fs # f1, f2, f3 = count() f1 = count() f2 = count() f3 = count() print(f1[0]()) print(f2[0]()) print(f3[0]())
结果仍然是:
9
9
9
调试后发现,运行f1[0]() 后:
因为f1[0]()中存放的就是函数f, 但此时i是3,故返回输出9!
所以返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。