代码环境:python3.6
装饰器是 python 的一种语法糖,本质是一个可调用的对象,其参数是一个被装饰的函数。装饰器可能会处理被装饰的函数并返回,或者将其替换成另外一个函数或对象。python
这种用法出如今不少 python web 框架中,例如把 url 地址映射到 http 响应的函数上的注册处。web
下面举个简单的例子:闭包
def register(func): # 内部无新函数 print('running register {}'.format(func)) return func @register def my_func(): print('running my_func()') if __name__ == "__main__": my_func()
大多数装饰器,一般内部会定义一个闭包结构的函数,将其返回替换被装饰函数。app
这种用法最多见,用于不修改原函数的基础上增长额外的功能,好比计算函数的运行时间、输出指定格式日志等。框架
下面用一个装饰器输出函数运行时间:函数
def running_time(func): # 内部有新函数 def print_running_time(*args): '''打印函数运行时间''' t0 = time.time() result = func(*args) need_time = time.time() - t0 print('新列表生成时间(秒):{:.8f}'.format(need_time)) return result return print_running_time @running_time def new_list(n): '''生成一个新列表''' temp_list = [] for x in range(n): temp_list.append(x * (x + 1)) return temp_list if __name__ == "__main__": print('新列表长度:{}'.format(len(new_list(12345)))) print('new_list 函数的 __name__ 属性:{}'.format(new_list.__name__)) print('new_list 函数的 __doc__ 属性:{}'.format(new_list.__doc__))
执行结果:url
running register <function my_func at 0x0000000002889B70> 新列表生成时间(秒):0.00250006 新列表长度:12345 new_list 函数的 __name__ 属性:print_running_time new_list 函数的 __doc__ 属性:打印函数运行时间
从上述例子中咱们注意到,在调用new_list(12345)
打印出结果以前,结果栏先输出了装饰器中的print
语句,这说明:日志
装饰器在导入模块@func
时当即执行。code
在上述结果中,咱们还注意到另外一个特色:new_list
函数的__name__
和__doc__
属性都被替换成装饰器内部函数的相关属性。因此,咱们须要改进上面的例子,使用functools.wraps
装饰器把相关属性从func
复制到新函数中。orm
改进例子以下:
from functools import wraps def running_time(func): # 内部有新函数 @wraps(func) # 此处使用 wraps 装饰器 def print_running_time(*args): '''打印函数运行时间''' t0 = time.time() result = func(*args) need_time = time.time() - t0 print('新列表生成时间(秒):{:.8f}'.format(need_time)) return result return print_running_time @running_time def new_list(n): '''生成一个新列表''' temp_list = [] for x in range(n): temp_list.append(x * (x + 1)) return temp_list if __name__ == "__main__": print('新列表长度:{}'.format(len(new_list(12345)))) print('new_list 函数的 __name__ 属性:{}'.format(new_list.__name__)) print('new_list 函数的 __doc__ 属性:{}'.format(new_list.__doc__))
执行结果:
running register <function my_func at 0x0000000002879B70> 新列表生成时间(秒):0.00250006 新列表长度:12345 new_list 函数的 __name__ 属性:new_list new_list 函数的 __doc__ 属性:生成一个新列表
观察改进后例子的运行结果,new_list
函数的相关属性已恢复正常。