python笔记36-装饰器之wraps
前言
前面一篇对python装饰器有了初步的了解了,但是还不够完美,领导看了后又提出了新的需求,希望运行的日志能显示出具体运行的哪个函数。
name和doc
__name__
用于获取函数的名称,__doc__
用于获取函数的docstring内容(函数的注释)
import time def func_a(a): '''func_a --> hello''' print("hello"+a) time.sleep(0.5) return True def func_b(b, c="xx"): '''func_b --> world''' print("world"+b+c) time.sleep(0.8) return True if __name__ == '__main__': print(func_a.__name__) # 结果 func_a print(func_a.__doc__) # func_a --> hello print(func_b.__name__) # func_b print(func_b.__doc__) # func_b --> world
装饰器加函数名称日志
在装饰器里面添加2行代码,打印正在运行函数的名称和docstring内容
import time def runtime(func): '''runtime decorators''' def wrapper(*args, **kwargs): '''wrapper inner fuction''' print("running function : %s" % func.__name__) print("docstring: %s" % func.__doc__) start = time.time() f = func(*args, **kwargs) # 原函数 end = time.time() print("运行时长:%.4f 秒" % (end-start)) return f return wrapper @runtime def func_a(a): '''func_a --> hello''' print("hello"+a) time.sleep(0.5) return True @runtime def func_b(b, c="xx"): '''func_b --> world''' print("world"+b+c) time.sleep(0.8) return True if __name__ == '__main__': func_a("a") print(func_a.__name__) print(func_a.__doc__)
运行结果
running function : func_a docstring: func_a --> hello helloa 运行时长:0.5008 秒 wrapper wrapper inner fuction
从运行的结果可以看出,func_a.__name__
运行的结果是wrapper,func_a.__doc__
运行的结果是wrapper inner fuction。
也就是说被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),那这个问题如何解决呢?
这就需要用到functools里面的一个wraps函数了
functools
当func_a函数被装饰后,导致了一个副作用:自身的函数属性和docstring内容变成了wrapper函数的属性了。
这里需用到functools里面的一个wraps的装饰器来消除这样的副作用。
import time from functools import wraps def runtime(func): '''runtime decorators''' @wraps(func) def wrapper(*args, **kwargs): '''wrapper inner fuction''' print("running function : %s" % func.__name__) print("docstring: %s" % func.__doc__) start = time.time() f = func(*args, **kwargs) # 原函数 end = time.time() print("运行时长:%.4f 秒" % (end-start)) return f return wrapper
只需在wrapper函数上加上@wraps(func)
即可解决
运行结果
running function : func_a docstring: func_a --> hello helloa 运行时长:0.5004 秒 func_a func_a --> hello
类装饰器
带参数的装饰器,可以写成类装饰器
import time from functools import wraps class runtime(object): '''runtime class decorators''' def __init__(self, slowly=1): self.slowly = slowly def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): '''wrapper inner fuction''' print("running function : %s" % func.__name__) print("docstring: %s" % func.__doc__) start = time.time() f = func(*args, **kwargs) # 原函数 end = time.time() t = end-start time.sleep((self.slowly-1)*t) # 延迟效果 new_end = time.time() print("运行时长:%.4f 秒" % (new_end-start)) return f return wrapper @runtime(1.5) def func_a(a): '''func_a --> hello''' print("hello"+a) time.sleep(0.5) return True @runtime() def func_b(b, c="xx"): '''func_b --> world''' print("world"+b+c) time.sleep(0.8) return True if __name__ == '__main__': func_a("a") print(func_a.__name__) print(func_a.__doc__)
运行结果
running function : func_a docstring: func_a --> hello helloa 运行时长:0.7522 秒 func_a func_a --> hello
2019年《python3自动化UI+接口》课程5月25-7月27开课主讲老师:上海-悠悠上课方式:QQ群视频在线教学上课时间:每周六、周日晚上20:30-22:30报名费:2000