上一篇装饰器(一)讲解了装饰器的基本用法,这篇我们来看一下装饰器的进阶用法。
一. 同一个函数调用多个装饰器
如果一个函数被多个装饰器装饰,也就是这个函数需要添加很多的功能,应该如何写呢?
import time
def get_time(foo):
def inner():
starttime = time.time()
foo()
endtime = time.time()
print("func2执行了%s秒" % (endtime - starttime))
return inner
def log(foo):
def inner():
print("%s执行了"%foo.__name__)
foo()
return inner
@log
@get_time
def func2():
print("我在执行func2")
func2()
执行结果为
我们来对这个调用顺序进行讲解:
对于装饰器的调用,离函数定义越近的装饰器越先调用。也就是先调用get_time,再调用log。那么
@get_time就等价于func2 = get_name(func2),返回了inner,一定要注意,这个时候inner并未执行!
@log等价于func2 = log(func2),也就是func2 = log(get_name(func2)),即func2 = log(inner)
二. 被调用函数有参数
def log(foo):
def inner():
print("%s执行了"%foo.__name__)
foo()
return inner
@log #func2(arg)=log(func2)(f2)
def func2(test):
print(test)
func2("我是func2")
如果被修饰函数func2有了参数,还是按之前的调用方式,是会报错的
报错原因在于,inner没有参数,那么可能会想,只要在inner加一个参数就好了嘛,但是这仅仅是满足了单个参数函数的需要,如果两个参数的函数,难道需要重写装饰器?这显然是不现实的,那Python给我们提供了一个函数的动态参数inner(*args,**kwargs)就可以不管被修饰函数的参数如何,都可以使用这一个装饰器。
def log(foo):
def inner(*args,**kwargs):
print("%s执行了"%foo.__name__)
foo(*args,**kwargs)
return inner
@log
def func2(test):
print(test)
func2("我是func2")
三. 装饰器完整模板
def log(foo):
def inner(*args,**kwargs):
print("%s执行了"%foo.__name__)
foo(*args,**kwargs)
return inner
@log
def func3(x,y):
return x+y
res = func3(2,3)
print(res)
对于上述代码我们执行后发现没有接收到func3的返回值
为什么呢?因为@log等价于fun3=log(fun3),到了装饰器函数发现,inner的确是没有返回值,那我们定义一个res来接受返回值再给它返回出去就好。我们这样改就可以了,接下来的装饰器便是完整的一个模板。
def log(foo):
def inner(*args,**kwargs):
print("%s执行了"%foo.__name__)
res = foo(*args,**kwargs)
return res
return inner
@log
def func3(x,y):
return x+y
res = func3(2,3)
print(res)
四. warps的使用
在实践中可能会出现这样的问题,被装饰器装饰的函数已经不是原来的那个函数了,如果我们想看一看原来函数里面的内容(例如注释)应该怎么办呢?
def deco(func):
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@deco
def index():
"注释"
print('from index')
print(index.__doc__) #它得不到注释的内容,因为index已经不是原来的函数了
解决这样的问题,我们只要使用wraps模块就可以解决了,并且在装饰器中添加@warps,就可以保留原来的函数里面的内容了。
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@deco
def index():
"注释"
print('from index')
print(index.__doc__)