装饰器(decorators)用于拓展原来函数功能的一种函数,目的是在不改变原来函数名(或类名)的情况下,给函数增加新功能。在Python中,装饰器的语法以@开头。
装饰器主要应用于:引入日志、函数执行时间统计、执行函数前的预备处理、执行函数后清理功能、权限校验、缓存。
【案例1】利用装饰器实现统计函数的执行时间
import time
def timeit(func): #func()为装饰器绑定的方法,绑定装饰器后自动传入
def result():
start_time = time.time()
func()
end_time = time.time()
print('函数运行时间为:%.2fs'%(end_time - start_time))
return result
@timeit
def func_0():
time.sleep(3)
@timeit
def func_1():
time.sleep(3)
func_0()
func_1()
函数运行时间为:3.01s
函数运行时间为:3.01s
【案例2】无参数的装饰器应用
import time
def timeit(func):
def result():
start_time = time.time()
func() #调用test方法
end_time = time.time()
print('函数运行时间为:%.2fs'%(end_time - start_time))
return result
@timeit
def test():
time.sleep(1)
print("测试函数运行")
test()
测试函数运行
函数运行时间为:1.00s
装饰器@timeit
会将test()函数
进行包装,实际上调用的是result()函数
。在result()函数
中,先调用了func()
,也就是test()函数
,然后再计算函数的运行时间并输出。因此,"测试函数运行"
会先输出,然后才会输出"函数运行时间为:1.00s"
。
【案例3】有参数的装饰器应用
import time
def timeit(func):
def result(arg1): #调用test方法的参数
start_time = time.time()
func(arg1) #调用test方法
end_time = time.time()
print('函数运行时间为:%.2fs'%(end_time - start_time))
return result
@timeit
def test(mStr):
time.sleep(2)
print("函数运行时间:" + mStr)
test("传入参数")
函数运行时间:传入参数
函数运行时间为:2.00s
【案例4】定义一个打印日志的装饰器
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
now()
execute now():
2015-3-25
上面的函数,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。
函数也是对象,它有__name__
等属性,但经过decorator装饰之后的函数,它们的__name__
已经从原来的'now'变成了'wrapper':
now.__name__
'wrapper'
因为返回的wrapper()函数
名字是'wrapper',所以,需要把原始函数的__name__
等属性复制到wrapper()函数
中,否则,有些依赖函数签名的代码执行就会出错。下:
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
即可实现,所以,一个完整的decorator的写法如下:
import functools
def log(text):
def decorator(func):
@functools.wraps(func) # wrapper()的前面加上@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
【案例5】装饰器可以修饰带参数函数,也可以修饰不带参数函数
import time
def timeit(func):
def result(*arg1,**kwargs): #传入非固定参数,这样即使装饰器函数不带参数也可以被装饰
start_time = time.time()
func(*arg1,**kwargs) #调用test方法
end_time = time.time()
print('函数运行时间为:%.2fs'%(end_time - start_time))
return result
@timeit
def test():
time.sleep(2)
print("函数运行")
test() #调用函数
函数运行
函数运行时间为:2.02s
【例题】设计一个decorator,可作用于任何函数上,并打印该函数的执行时间:
import time, functools
def metric(fn):
@functools.wraps(fn)
def result(*args,**kwargs):
start_time = time.time()
res = fn(*args, **kwargs)
end_time = time.time()
print('%s executed in %.fms' % (fn.__name__, (end_time - start_time) * 1000))
return res # 将fn(*args, **kwargs)的返回值返回给调用者
return result
# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
fast executed in 11ms
slow executed in 135ms
评论 (0)