python装饰器 Python 装饰器详解_python 带参数的装饰器-CSDN博客
对装饰器@wraps的解释(一看就懂)— 并对装饰器详解 - 交流_QQ_2240410488 - 博客园 (cnblogs.com)
1. 为什么需要装饰器呢? Leader让小A写两个数字相加和相减的函数,小A很快就写完了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def add (x, y ): return x + y def sub (x, y ): return x - y if __name__ == '__main__' : result = add(1 , 2 ) print (result) result = sub(5 , 4 ) print (result)
Leader让小A添加上统计函数的运行时长的功能, 小A直接在调用函数时加上了时长的计算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import timedef add (x, y ): return x + y def sub (x, y ): return x - y if __name__ == '__main__' : start = time.time() result_1 = add(1 , 2 ) end = time.time() print ('result: %d' % result_1) print ('time taken %f' % (end - start)) start = time.time() result_2 = sub(5 , 4 ) end = time.time() print ('result: %d' % result_2) print ('time taken %f' % (end - start))
Leader看了,说每次调用函数岂不是要写很多重复代码吗。小A进行了优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import timedef add (x, y ): start = time.time() rv = x + y end = time.time() print ('time taken %f' % (end - start)) return rv def sub (x, y ): start = time.time() rv = x - y end = time.time() print ('time taken %f' % (end - start)) return rv if __name__ == '__main__' : result_1 = add(1 , 2 ) print ('result: %d' % result_1) result_2 = sub(5 , 4 ) print ('result: %d' % result_2)
这种方法肯定比前一种要好。但是当我们有多个函数时,那么这似乎就不方便了。
小A又定义了一个计时的函数并包装其他函数,然后返回包装后的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import timedef time_taken (func ): def inner (*args, **kwargs ): start = time.time() rv = func(*args, **kwargs) end = time.time() print ('time taken %f' % (end - start)) return rv return inner def add (x, y ): return x + y def sub (x, y ): return x - y if __name__ == '__main__' : add = time_taken(add) result_1 = add(1 , 2 ) print ('result: %d' % result_1) sub = time_taken(sub) result_2 = sub(5 , 4 ) print ('result: %d' % result_2)
Leader说上面的解决方案以及非常接近装饰器的思想了,小A查了一下装饰器的用法,加入装饰器后代码果然变得很优雅。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import timedef time_taken (func ): def inner (*args, **kwargs ): start = time.time() rv = func(*args, **kwargs) end = time.time() print ('time taken %f' % (end - start)) return rv return inner @time_taken def add (x, y ): return x + y @time_taken def sub (x, y ): return x - y if __name__ == '__main__' : result_1 = add(1 , 2 ) print ('result: %d' % result_1) result_2 = sub(5 , 4 ) print ('result: %d' % result_2)
2. 类装饰器 装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志 、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
2.1 不带参数的类装饰器 基于类装饰器的实现,必须实现__call__
和__init__
两个内置函数。__init__
:接收被装饰函数__call__
:实现装饰逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class logger (object ): def __init__ (self,func ): self.func = func def __call__ (self,*args,**kwargs ): print 'the function {}() is running...' \ .format (self.func.__name__) return self.func(*args,**kwargs) @logger def say (something ): print 'say {}!' .format (something) say('hello' )
运行结果如下:
1 2 Copythe function say() is running... say hello!
2.2 带参数的类装饰器 带参数和不带参数的类装饰器有很大的不同。__init__
:不再接收被装饰函数,而是接收传入参数__call__
:接收被装饰函数,实现装饰逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class logger (object ): def __init__ (self,level='INFO' ): self.level = level def __call__ (self,func ): def wrapper (*args,**kwargs ): print '{level}: the function {func} () is running...' \ .format (level=self.level, func=func.__name__) func(*args,**kwargs) return wrapper @logger(level='WARNING' ) def say (something ): print 'say {}!' .format (something) say('hello' )
运行结果如下:
1 2 CopyWARNING: the function say () is running... say hello!
2.3 wraps语法糖 使用这个语法糖保留函数原来的属性,name ,doc 等属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from functools import wrapsdef logging (func ): @wraps(func ) def inner (*args, **kwargs ): """logging.inner""" if type == 'debug' : print ('[DEBUG] logging' ) else : print ('[INFO] logging' ) rv = func(*args, **kwargs) return rv return inner @logging def add (x, y ): """Add x and y""" return x + y if __name__ == '__main__' : print (add.__name__) print (add.__doc__)
2.4 打印日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 from functools import wrapsimport timefrom random import randintdef record (output ): def use_time (func ): @wraps(func ) def wrapper (*args,**kwargs ): st_time = time.time() result = func(*args,**kwargs) end_time = time.time() output(func.__name__, end_time-st_time) return wrapper return use_time @record(print ) def foo (): time.sleep(randint(2 ,5 )) for _ in range (3 ): foo() >>>> foo 3.000645875930786 foo 4.003818988800049 foo 2.0020666122436523 def write_log (name,content ): with open ('./time.log' ,'a' )as f: f.write(f'{name} 耗时:{content} \r\n' ) @record(write_log ) def foo (): time.sleep(randint(2 ,5 )) for _ in range (3 ): foo()