书接闭包。
闭包,是将函数嵌套后以内层函数引用作为返回值返回,通过外部函数的调用去实现内部函数的调用。而装饰器,则是在闭包的基础上,将外层函数的参数设置为另一个外部函数。
我们已经知道,闭包可以灵活使用,指定不同外层函数的参数作为默认参数打成闭包实例后可以成为不同的函数实现不同的功能,但是不改变嵌套函数的实现逻辑。
上一篇闭包篇中举的例子,是关于嵌套函数自身,保留外层函数运行状态来用的。装饰器的应用思路,则是:
- 存在一个原函数,在原函数之外另创建一个闭包,这个闭包的外层函数参数类型是函数;
- 将原函数作为闭包的外层函数参数传入,然后通过闭包的运行逻辑由外层函数将原函数传递进内层函数;
- 内层函数本身有别的更多代码功能,等于把原函数按需插入了内层函数的运行环节里,作为整个闭包的其中一环;
- 然后由此闭包给原函数名变量赋值,暂时用命名覆盖掉原函数,这样就实现了不改动原函数实际内容却可以实现在原函数的基础上添加新的功能(也就是前面提到的写在闭包嵌套函数的内层函数里的更多代码功能)。
所以,装饰器的名字其实很形象,它就是对原函数进行包裹装饰,只累加功能改变,而不原地改变。
举个例子
import time
def timer(func):
def call_func():
print("开始运行程序...")
start = time.time()
func()
stop = time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
def test():
time.sleep(2)
print("I love Python.")
test = timer(test)
test()
这个例子里,test函数功能是让程序沉睡2s。
现在我们想要给这个程序加一个计时功能,那么我们就可以在内层函数里写一个能够取到程序运行前时间和运行后时间然后作差的功能,同时外层函数参数类型设置为函数类型,然后将内层函数返回出去,这样我们就得到了一个有计时功能的装饰器。
按照创建闭包实例的方法,将test函数传入装饰器中,得到实例,再将实例在原函数之后赋给原函数同名的变量,于是便可在形式上暂时覆盖原函数,得到装饰过原函数的同名引用(引用,即将函数作为变量赋值给变量名,此时通过这个变量名便可引用该函数)。再用这个名字调用时,运行的便是装饰后的新函数。
装饰器有一个语法糖,为了更清楚展示如何替换的这里整个代码重新放一下:
import time
def timer(func):
def call_func():
print("开始运行程序...")
start = time.time()
func()
stop = time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
@timer #
def test():
time.sleep(2)
print("I love Python.")
#test = timer(test)
test()
>>>开始运行程序...
I love Python.
结束程序运行...
一共耗费了2.01秒。