代码环境:python3.6
什么是装饰器
装饰器是 python 的一种语法糖,本质是一个可调用的对象,其参数是一个被装饰的函数。装饰器可能会处理被装饰的函数并返回,或者将其替换成另一个函数或对象。
装饰器两种常用的用法
- 原封不动返回被装饰函数
这种用法出现在很多 python web 框架中,例如把 url 地址映射到 http 响应的函数上的注册处。
下面举个简单的例子:
def register(func):
# 内部无新函数
print('running register {}'.format(func))
return func
@register
def my_func():
print('running my_func()')
if __name__ == "__main__":
my_func()
- 把被装饰函数替换成新函数
大多数装饰器,通常内部会定义一个闭包结构的函数,将其返回替换被装饰函数。
这种用法最常见,用于不修改原函数的基础上增加额外的功能,比如计算函数的运行时间、输出指定格式日志等。
下面用一个装饰器输出函数运行时间:
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__))
执行结果:
running register <function my_func at 0x0000000002889B70>
新列表生成时间(秒):0.00250006
新列表长度:12345
new_list 函数的 __name__ 属性:print_running_time
new_list 函数的 __doc__ 属性:打印函数运行时间
python何时执行装饰器
从上述例子中我们注意到,在调用new_list(12345)
打印出结果之前,结果栏先输出了装饰器中的print
语句,这说明:
装饰器在导入模块@func
时立即执行。
functools.wraps装饰器
在上述结果中,我们还注意到另一个特点:new_list
函数的__name__
和__doc__
属性都被替换成装饰器内部函数的相关属性。所以,我们需要改进上面的例子,使用functools.wraps
装饰器把相关属性从func
复制到新函数中。
改进例子如下:
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
函数的相关属性已恢复正常。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。