python学习之路(18)
返回函数
函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:
>>> def a(*args): ax=0 for n in args: ax =ax+n return ax >>> a([1,2,4]) Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> a([1,2,4]) File "<pyshell#5>", line 4, in a ax =ax+n TypeError: unsupported operand type(s) for +: 'int' and 'list' >>> a(1,2,3,4) 10
这里不能用list传参 注意是*args
但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!
>>> def a(*args): def sum(): ax=0 for n in args: ax=ax+n return ax return sum
当我们调用a()
时,返回的并不是求和结果,而是求和函数:调用函数s
时,才真正计算求和的结果:
>>> s=a(1,2) >>> s <function a.<locals>.sum at 0x05D14C00> >>> s() 3
在这个例子中,我们在函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> s1=a(1,2) >>> s2=a(1,2) >>> s1==s2 False
s1()
和s2()
的调用结果互不影响。
闭包
注意到返回的函数在其定义内部引用了局部变量args
,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()
才执行。我们来看一个例子:
>>> def p(): fs=[] for i in range(1,4): def f(): return i*i fs.append(f) return fs >>> f1,f2,f3 =count() Traceback (most recent call last): File "<pyshell#39>", line 1, in <module> f1,f2,f3 =count() NameError: name 'count' is not defined >>> f1,f2,f3 =p() >>> f1() 9 >>> f2() 9 >>> f3() 9
全部都是9
!原因就在于返回的函数引用了变量i
,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i
已经变成了3,因此最终结果为9
。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
>>> def count(): fs=[] for i in range(1,4): def f(j): def g(): return j*j return g fs.append(f(i)) return fs >>> f1=count() >>> f1() Traceback (most recent call last): File "<pyshell#33>", line 1, in <module> f1() TypeError: 'list' object is not callable >>> f1, f2, f3 = count() SyntaxError: unexpected indent >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9
对于@塔塔gogo的例子,说白了就是i是公共的,j是私有的。也就是
i=1
j=i #f1可使用这个j,以及公共的i
i=2
j=i #f2可使用这个j,以及公共的i
i=3
j=i #f3可使用这个j,以及公共的i
虽然表面上看有3个j,然而这3个j在内存中具有不同的地址,而公共的i只有一个地址。
最后计算f1()时,虽然此时i=3,但是f1中的j为1,从而得到结果1;同理f2()为4,f3()为9.
下面将这个程序稍作修改,结果显而易见。
count()中存放的是三个f函数,当调用f1,f2,f3 = count()时,此时的for循环结束了,i=3.再执行f1()的时候相当于执行下面: i=3 def f(): return i*i 这个时候的值为9.
其实count()执行后,是返回一个这样的list【g(j1),g(j2),g(j3)】,而不是【f(j1),f(j2),f(j3)】。然后>>> f1, f2, f3 = count()分别指向list中的每个元素。
lambda简化函数
f1, f2, f3 = map(lambda y : (lambda : y * y), range(1,4))