定义
在不改变原函数的调用方式的情况下,在函数前后添加功能
一、完美的装饰器
""" 完美的装饰器 """ import functools import time def timer(func): @functools.wraps(func) def wrap(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() time_interval = end_time - start_time print('【%s】 函数 调用了 %s 秒' % (func.__name__, time_interval)) return ret return wrap # 装饰器等价于:hello_world = timer(hello_world) @timer def hello_world(): time.sleep(0.2) print('hello world') return 'ok' print(hello_world()) # print(hello_world.__name__) # @functools.wraps(func)的作用:使hello_world.__name__正确显示
二、带参数的装饰器
""" 需求:要求装饰器装饰函数的时候,可以传一个值,这个值表示 timer 这个装饰器是否启用:(True 启用)(False 不启用) 使用过的场景:functools.wraps """ import time from functools import wraps def timer(flag): def inner_timer(func): @wraps(func) def wrap(*args, **kwargs): if flag: start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print('%s 程序运行了 %s 秒' % (func.__name__, end_time - start_time)) return ret else: return func(*args, **kwargs) return wrap return inner_timer # 装饰器等价于:hello_world = timer(True)(hello_world) @timer(True) def hello_world(): time.sleep(0.2) return 'ok' print(hello_world()) # print(hello_world.__name__)
三、多个装饰器装饰一个函数
""" 多个装饰器装饰一个函数,谁离被装饰的函数近,谁就先装饰。 """ import time from functools import wraps def wrap1(func): @wraps(func) def inner(*args, **kwargs): print('wrap1 装饰前') ret = func(*args, **kwargs) print('wrap1 装饰后') return ret return inner def wrap2(func): @wraps(func) def inner(*args, **kwargs): print('wrap2 装饰前') ret = func(*args, **kwargs) print('wrap2 装饰后') return ret return inner @wrap1 @wrap2 def hello_world(): time.sleep(0.3) print('hello world') hello_world() """ 输出: wrap1 装饰前 wrap2 装饰前 hello world wrap2 装饰后 wrap1 装饰后 """
补充:完美的装饰器
经过装饰器装饰之后的函数,他们的__name__已经从原来的'now'变成了'inner'
1 def wrapper(func): 2 def inner(*args, **kwargs): 3 print("在调用被装饰的函数前执行的代码") 4 ret = func(*args, **kwargs) 5 print("在调用被装饰的函数前执行的代码") 6 return ret 7 return inner 8 9 10 @wrapper 11 def now(): 12 print('2018-8-19') 13 14 15 print(now.__name__) # inner
因为返回的那个inner()
函数名字就是'inner'
,所以,需要把原始函数的__name__
等属性复制到inner()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的decorator的写法如下:
1 import functools 2 3 4 def wrapper(func): 5 @functools.wraps(func) 6 def inner(*args, **kwargs): 7 print("在调用被装饰的函数前执行的代码") 8 ret = func(*args, **kwargs) 9 print("在调用被装饰的函数前执行的代码") 10 return ret 11 return inner 12 13 14 @wrapper 15 def now(): 16 print('2018-8-19') 17 18 19 print(now.__name__) # now