一:返回函数
定义:函数可以作为另一个函数的返回值。
理论来源:函数可以嵌套定义。
def fun1(li):
def fun2():
return sum(li)
return fun2
调用fun1函数,返回的是一个一个fun2实例。
获取最终结果,需要执行:
test()
二:闭包
定义:在一个内部函数里,对外在作用域(不是全局作用域)的变量进行引用,这种程序结构被称为闭包。
举例:上例中的fun2是一个内部函数,它引用了li这个外部变量,li的作用域仅在fun1中,所以fun2,就是一个闭包。
闭包和返回函数的关系:闭包以返回函数的形式实现。
注意:闭包,是函数体和引用环境的整体。
警告:闭包中尽量不要引用循环变量,或后期会发生变化的变量。
实例:
def fun3():
f=[]
for i in range(1,4): #循环遍历1,2,3
def fun4():
return i**2 #返回i的二次幂
f.append(fun4) #将结果追加到列表f中
return f
测试代码如下:
for i in fun3():
print(i())
原以为会返回一个[1,4,9]的列表,但是结果为[9,9,9]
这是因为,其实f.append(fun4)这行代码,不是将结果追加到了列表中,而是将fun4函数追加到列表中。
最终的列表f,每一个元素,都是一个闭包。
闭包是函数和运行环境的结合,在这里,函数是i**2,而环境,就是i。当三个元素被追加完时,i已经变成了3。
所以,列表f的每一个元素都是3**2,也就是9
解决这类问题的方向是:增加一层隔离环境。
通过上面的例子我们明白,结果出现偏差的主要原因是,运行环境发生变化(i发生了变化)。那么,可以这样解决:
def fun3():
f=[]
def fun4(j):
def fun5():
return j ** 2 # 返回i的二次幂
return fun5 #返回一个闭包,函数为j**2,运行环境为j
for i in range(1,4): #循环遍历1,2,3
f.append(fun4(i)) #追加元素到f,每一个元素为一个闭包,在闭包内,变量恒为i
return f
三:装饰器
定义:在代码运行期间,动态增加功能的方式,成为装饰器。
本质:装饰器本质上是一个返回函数的高阶函数。
与闭包的关系:是闭包的一种应用场景。
定义装饰器的两种方法:
1:利用闭包原理
双层嵌套:定义一个装饰器,接收函数作为参数,返回一个warpper闭包,在warpper内部,定义在函数执行前或后进行的操作。
实例:
import time
def log(fun): #定义一个装饰器,接收函数fun为参数
def wrapper(*args,**kwargs): #内部函数wrapper,定义具体的装饰操作
print('excute %s'%fun.__name__) #在函数执行前,打印excute 函数名
return fun(*args,**kwargs) #执行函数
return wrapper
@log #用@为函数添加装饰器,相当于abc=log(abc)
def abc(): #abc=log(abc),执行wrapper闭包,首先打印excute abc
time.sleep(1) #执行函数本身,等待一秒
print('my name is abc') #执行打印方法
if __name__=='__main__':
abc()
三层嵌套:适用于装饰器本身需要传入参数。
def log(text): #定义一个装饰器,接收普通参数text
def decorator(fun): #定义一个高阶函数,接收函数为参数
def wrapper(*args,**kwargs): #内部函数wrapper,定义具体的装饰操作
print('%s %s'%(text,fun.__name__)) #在函数执行前,打印text 函数名
return fun(*args,**kwargs) #执行函数
return wrapper #返回wrapper闭包
return decorator #返回decorator闭包
@log('ready,go!') #用@为函数添加装饰器,并携带参数,相当于abc=log('ready go!')(abc)
def abc(): #abc=log('ready go!')(abc),执行decorator闭包,再执行wrapper闭包,首先打印ready go! abc
time.sleep(1) #执行函数本身,等待一秒
print('my name is abc') #执行打印方法
2:类装饰器
class log(object):
def __init__(self,fun): #实例化需要接收一个函数
self.fun=fun #将函数赋给类的fun属性,相当于接收一个函数作为参数
def __call__(self, *args, **kwargs): #能像方法一样被调用
print('excute %s'%self.fun.__name__)#在函数执行前打印
return self.fun() #执行函数
@log #abc=log(abc),将abc作为log类的一个实例
def abc(): #abc已经变成一个Log对象,调用__call__方法,先打印excute abc
time.sleep(1) #睡眠
print('my name is abc') #打印my name is abc