学习笔记
平时用到Python装饰器的地方比较少,最近写一个项目,用到了装饰器。在此把学习过程及要点记录一下。如果你正在熟悉装饰器相关内容,希望它能给到你一些帮助。
参考资料
1. 装饰器是一个特殊的函数
如果我们想在已有的函数的基础上再增加某个功能(装饰一下),那么可以采用如下的方式:
def func(param):
# do something.
def decorating(specified_func):
def wrapper(params):
# before calling func, do something ....
specified_func(params)
# after calling func, do something ...
return wrapper
if __name__ == "__main__":
decorated_func = decorating(func)
decorated_func(args)
函数decorating
在func
的基础上装饰了某些新功能,形成被装饰的函数对象decorated_func
。 除了附加功能外,函数对象decorated_func
调用方式和原来的func
完全一样。
python中对此提供了一个语法糖,简化了编程书写形式:
def decorating(specified_func):
def wrapper(params):
# before calling func, do something ....
specified_func(params)
# after calling func, do something ...
return wrapper
@decorating
def func(param):
# do something.
if __name__ == "__main__":
func(args)
此时,调用func
和上面的例子中调用decorated_func
是完全一样的。
2. 带参装饰器
我们在上面的例子中再包装一层,就形成了带参装饰器。如下:
def func(params):
# do something ...
def decorating_with_param(decorator_params):
def decorating(specified_func):
def wrapper(params):
#before call func, do something with decorator_params...
specified_func(params)
#after call func, do something with decorator params ...
return wrapper
return decorating
if __name__ == "__main__":
arg_decorated_func = decorating_with_param(decorator_args)(func)
arg_decorated_func(args)
同样的, 使用装饰器的书写形式,就变成了:
def decorating_with_param(decorator_params):
def decorating(specified_func):
def wrapper(params):
#before call func, do something with decorator_params...
specified_func(params)
#after call func, do something with decorator params ...
return wrapper
return decorating
@decorating_with_param(decorator_args)
def func(param):
# do something.
if __name__ == "__main__":
func(args)
此时,调用func
和调用arg_decorated_func
是完全一样的,仅仅是书写方式不一样而已。
3. 可传递任意参数
为了可以传递任意参数,装饰器的函数参数一般写为如下形式:
def decorating(func):
def wrapper(*args, **kwargs):
#before call func, do something here.
func(*args, **kwargs)
#after call func, do something ...
return wrapper
4. 装饰器的修饰顺序
如果有多个装饰器同时修饰一个函数,则离函数最近的装饰器最先应用。例如:
@C
@B
@A
def func(param):
#do something.
if __name__ == "__main__":
func(args)
此时调用func
的作用等价于:
def func(param):
# do something..
if __name__ == "__main__":
dec_a_func = A(func)
dec_b_func = B(dec_a_func)
dec_c_func = C(dec_b_func)
dec_c_func(args)
5. 一个示例
下面这个例子中,func
仅仅休眠指定的时间(s), timeit
装饰器打印func
开始与结束,并记录单词执行时间,repeate
重复执行n次,并记录总时间。
def timeit(func):
def inner(*args, **kwords):
print("------begin call {} -------".format(func))
t1 = time.time()
func(*args, **kwords)
t2 = time.time()
print("------end {} using %ds.-------".format(func) %(t2-t1))
return inner
def repeat(iterating):
def timeit(func):
def inner(*args, **kwords):
t1 = time.time()
for _ in range(iterating):
func(*args, **kwords)
t2 = time.time()
print("iterating %d time(s) using %ds." %(iterating, t2-t1))
return inner
return timeit
# @timeit
@repeat(2)
@timeit
def doSomething(n):
time.sleep(n)
if __name__ == "__main__":
doSomething(3)
执行结果如下:
joly@ubuntu:~/Workspace/Flask-Learning/4.装饰器$ python3 timeit.py
------begin call <function doSomething at 0x7f2d589630d0> -------
------end <function doSomething at 0x7f2d589630d0> using 3s.-------
------begin call <function doSomething at 0x7f2d589630d0> -------
------end <function doSomething at 0x7f2d589630d0> using 3s.-------
iterating 2 time(s) using 6s.
结束。