liushuibufuqin 2018-02-05
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
我们要需要一个能测试函数运行时间的decorator,可以定义如下:
def timer(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print("Run time is: %s" % (end_time - start_time)) return res return wrapper
因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
@timer def fun(): time.sleep(1) print("This is a test") return "OK"
运行结果就是:
This is a test Run time is: 1.0000572204589844 OK
把@timer放在fun()处就相当于执行了语句:
fun = timer(fun)
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,比如加上名字:
def timer(name): def decorator(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print("Name is: %s ; Run time is: %s" % (name, (end_time - start_time))) return res return wrapper return decorator
调用及结果显示如下:
@timer("Lance#") def fun(): time.sleep(1) print("This is a test") return "OK"
This is a test Name is: Lance# ; Run time is: 1.0000572204589844 OK
和两层嵌套的decorator相比,三层嵌套的效果是这样的:
fun = timer("Lance#")(fun)
因为函数也是对象,它也有__name__等属性。
在未加装饰器之前的fun()函数,调用 fun的__name__属性结果是 'fun',但经过decorator装饰之后的函数,它们的__name__已经从原来的'fun'变成了'wrapper'
所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
Python内置的functools.wraps就可以完成这个任务,所以,一个完整的decorator的写法如下:
import functools def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print("Run time is: %s" % (end_time - start_time)) return res return wrapper
整体代码如下: