最近在看python的高级教程,被装饰器这个概念难住了,查了许多相关的资料后,对装饰器有了进一步的了解。
装饰器本质上是一个的函数,一个比较特殊的函数,是一个参数是函数,返回值也是函数的函数。
装饰器的作用是在不改变被装饰函数的前提下,用内部函数(FuncHandler
)对被装饰函数进行处理,常用于对一个函数的安全性、性能等运行情况进行检测。
一、基础的装饰器函数
1.装饰器基础
def decorator(func):
def funcHandler():
# Do something...
func()
return funcHandler
@decorator
def test():
pass
上面代码中的decorator
可以对test()
函数相应的自定义处理(Do somthing…)。下面通过print
语句模拟函数的执行过程。
def decorator(func):
print("I am decorator!")
def funcHandler():
print("I am funcHandler!")
func()
return
return funcHandler
@decorator
def decoratedFunc():
print("I am decoratedFunc!")
if __name__ == '__main__':
mainFunc = decoratedFunc
mainFunc()
输出
I am decorator!
I am funcHandler!
I am decoratedFunc!
根据执行结果可以知道,被装饰的函数(decoratedFunc
)作为实际参数传入decorator(func)
中,返回funcHandler
函数,执行decoratedFunc()
即执行decorator()
整体。
2.装饰器示例
根据装饰器的性质,可以写一个用于计算函数运行时间的简单的装饰器Timeit
。代码如下,
import time
def Timeit(func):
def handeler():
print("开始测试...")
start = time.time()
func()
return time.time() - start
return handeler
@Timeit
def testFunc():
time.sleep(2)
if __name__ == '__main__':
# Timeit
Result = testFunc()
#等效于
# Result = Timeit(testFunc)
print(f"运行时间为:{Result} s")
输出
开始测试...
运行时间为:2.005072832107544 s
不使用"@",代码等价于
import time
def Timeit(func):
def handeler():
print("开始测试...")
start = time.time()
func()
return time.time() - start
return handeler
def decoratedFunc():
time.sleep(2)
if __name__ == '__main__':
# Timeit
func = Timeit(decoratedFunc)
Result = func()
print(f"运行时间为:{Result} s")
二、带参数的装饰器
1.增加测试次数
在上面函数计时器的基础上,想要增加测试次数(比如说测试100次,1000次以增加结果的可靠性),可以给装饰器增加参数。此时需要给原有的装饰器再“套一层”,以下面的函数计时装饰器为例,
import time
def Timeits(times=10): # 指定默认参数(测试次数)为10
def decorator(func):
def handler():
totalTime = 0
print("开始测试...")
for i in range(times):
start = time.time()
func()
result = time.time() - start
totalTime += result
return totalTime/times
return handler
return decorator
@Timeits(100) # 在此处设置参数,需要对函数测试100次
def decoratedFuncs():
# print("I")
time.sleep(1)
if __name__ == '__main__':
# Handler = decoratedFuncs
# Result = Handler() # 或
Result = decoratedFuncs()
print(Result)
输出:
开始测试...
1.005906867980957
带参数的装饰器,是在基础装饰器的基础上再“套一层”用于传入参数,传入参数后,实际的装饰器是再近一层的函数,如本例中的decorator(func)
函数。
2.计时装饰器终极版
除了设置测试次数外,还可以以可变参数的形式为被装饰函数传入参数,以增加计时器的普适性。示例代码如下
import time
def Timeit(number):
def decorator(func):
def handler():
totalTime = 0
print("开始测试...")
for i in range(number):
start = time.time()
func()
result = time.time() - start
totalTime += result
return totalTime / number
return handler
return decorator
def FinalTimeit(testTimes, func, **kwargs):
@Timeit(testTimes)
def decoratedFunc():
return func(**kwargs)
# start = time.time()
result = decoratedFunc()
return result
if __name__ == '__main__':
def mainFunc(number):
time.sleep(number)
sleepTime = 1
Start = time.time()
Result = FinalTimeit(testTimes=10, func=mainFunc, number=sleepTime)
print(f"被测函数的平均单次运行耗时{Result}s")
print(f"测试总共耗时{time.time()-Start}s")
输出
开始测试...
函数运行平均耗时1.0068019151687622s
测试总共耗时10.068019151687622s
修改后,直接调用FinalTimeit()
函数,传入测试次数(testTimes
),测试函数(func
),和测试函数的参数(如上面的number=sleepTime
),即可返回函数的平均单次运行时间