在了解装饰器之前,我们先来了解一下闭包,因为装饰器的本质也是闭包。
1、闭包
闭包:一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行,这样一个函数称之为闭包。概念比较抽象,我们直接看代码:
def outer_func():
loc_list = []
def inner_func(name):
loc_list.append(name)
print(loc_list)
return inner_func
func = outer_func()
func("A")
func("B")
output:
['A']
['A', 'B']
inner_func函数引用了函数外定义的变量loc_list,并且该函数可以在outer_func外被执行,所以inner_func是一个闭包。
2、装饰器
装饰器本质也是一个闭包,装饰器特别之处在于使用函数作为参数。接下来直接看看装饰器是如何定义和被调用的吧。
如下面的例子:用装饰器实现任意函数计时和函数峰值内存使用的统计;
import time
from functools import wraps
def cdTime(func):
@wraps(func)
def timeCount():
startTime = time.time()
result = func()
endTime = time.time()
print(f"函数{func.__name__}耗时{endTime-startTime}秒。")
return result
return timeCount
import psutil
def cdMum(func):
@wraps(func)
def mumCount():
mum = psutil.virtual_memory()
total = mum.total
result = func()
print(f"函数{func.__name__}消耗内存{mum.used}字节。")
return result
return mumCount
@cdTime
@cdMum
def sumN():
sum = 0
for i in range(10000000):
sum += i
return sum
sumN()
如上述代码,装饰器的使用是直接在函数前使用(@ + 函数名)的形式进行调用,如@cdTime、@cdMum,而且装饰器接收调用装饰器的函数名作为参数(sumN),使得其内部可以调用外部函数,装饰器最终返回闭包函数;
这里还使用了一个装饰器@wraps(func),是因为装饰器返回的是闭包函数,那原函数(sumN)的名称什么的都变成了闭包函数,这样的话不方便程序debug等,所以使用@wraps(func)返回原函数的名称和地址。