上次说到高阶函数是接收函数作为参数或将函数作为返回值的函数,并且学习了接收函数作为参数的高阶函数,所以本次学习聚焦于第二种,也就是将函数作为返回值的高阶函数。
另一种高阶函数
先举一个这种高阶函数的简单例子。
def gaojie():
q = '环首刀'
def didi():
print(q)
return didi
w = gaojie()
w()
环首刀
这个变量q的值根据前面命名空间的东西可以知道,只有didi()函数能访问,外部无法访问,所以就形成了一个闭包,就是这种高阶函数的别称。创建闭包的目的在于创建一些只有当前函数能访问的变量,用于藏匿私有数据。比如一些需要设置变量初始值的,而后续使用函数初始变量会不断改变而且不希望被外部干扰的函数。
def qiupingjunshu():
suoqiu = []
def suanpingjunshu(i):
suoqiu.append(i)
return sum(suoqiu)/len(suoqiu)
return suanpingjunshu
r = qiupingjunshu()
print(r(10))
print(r(20))
suoqiu = [10]
print(r(30))
10.0
15.0
20.0
可以看出外部干扰suoqiu = [10]这种语句无法对继续调用r这个闭包产生任何影响。
综上所述,创建闭包需要满足一些要素:嵌套格式的函数;将内部函数作为返回值返回;内部函数必须使用到外部函数的变量。
装饰器
说白了就是装饰函数用的,因为调用代码不一定输出,函数工作以后可能什么也不会显示,装饰器就可以提示用户函数已经运行了。这种提示可以直接写在函数里面,但是函数很多那一个个写就很繁琐,而且函数发生改动还得一个个修改提示信息。
另外还违反开闭原则(OCP),即设计出的程序只能扩展但是不能修改的原则。所谓扩展,就是在别的函数里面调用这个函数,所以不是修改函数。例如,我创建一个加法函数,那么别人就不能在这个函数里面加入其他语句,但是别人可以定义一个新函数,把我的函数放进去,这样在调用我的函数以外的语句中写其他内容就可以了,这就叫扩展:
def jiafa(a,b): # 这里面不能写别的了
r = a + b
return r
def xinjiafa(a,b): # 但是这里面可以
print('start')
r = jiafa(a,b)
print('end')
return r
r = xinjiafa(111,222)
print(r)
start
end
333
注意,在函数里面调用其他函数需要注意你返回的是什么,比如调用xinjiafa(a,b)函数,你想要的实际上是jiafa(a,b)函数的返回值,所以要记得return jiafa(a,b)这一步。另外注意,这种函数扩展的过程不同于上面讲到的高阶函数,因为那种是函数嵌套在内定义的,而这种是定义在外面,所以新函数的形参也需要和里面要调用的参数保持一致,但是不需要和原定义的参数形参字母一样,比如xinjiafa(a,b)可以修改为xinjiafa(c,d),只要把里面调用命令r = jiafa(a,b)也改成r = jiafa(c,d)保持形参一致就行了。
上面说完OCP原则的问题,下面就是解决普遍装饰的问题,因为一个个改不现实,太费力。可以用函数解决这个问题,用创造装饰器的函数装饰所有函数,也就是用这个函数扩展所有函数。
def jiafa(a,b): # 这里面不能写别的了
r = a + b
return r
def fn():
print('suck')
def zhuangshiqi(k):
'''
作用是扩展其他函数,显示函数的开始和结束
参数:
k代表要扩展的函数对象,注意是函数对象,没括号
:return:
'''
def zhuangshi(*args,**kwargs): # 对于这个函数,内部没有再嵌套其他定义函数了,所以内外需要参数一致,但是外部zhuangshiqi()的参数就不需要和这个的一致,因为这个函数嵌套在人家里面
print('执行已开始') # *args,**kwargs分别接收所有位置参数和关键字参数,有没有都行,所以可以传输所有函数
r = k(*args,**kwargs)
print('执行已结束')
return r
return zhuangshi
f = zhuangshiqi(fn)
f1 = zhuangshiqi(jiafa)
r = f() # 注意调用装饰器函数的时候需要看输入的函数是否有参数,有的话你也要输入实参。
r1 = f1(111,222)
print(r1)
执行已开始
suck
执行已结束
执行已开始
执行已结束
333
或者在定义函数之前加一个@装饰器函数对象就行了。
@zhuangshiqi
def fn():
print('suck')
fn()
执行已开始
suck
执行已结束
可以@好几个装饰器,这样会被多个装饰器装饰,顺序是从内向外,或者由近及远,就是靠近下面函数的装饰器的装饰效果也离函数调用结果最近。