装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
1 import time 2 3 def show_time(func): 4 def wrapper(): 5 start_time=time.time() 6 func() 7 end_time=time.time() 8 print('spend %s'%(end_time-start_time)) 9 10 return wrapper 11 12 13 def foo(): 14 print('hello foo') 15 time.sleep(3) 16 17 foo=show_time(foo) 18 foo()
函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像foo被上下时间函数装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作
1 import time 2 3 def show_time(func): 4 def wrapper(): 5 start_time=time.time() 6 func() 7 end_time=time.time() 8 print('spend %s'%(end_time-start_time)) 9 10 return wrapper 11 12 @show_time #foo=show_time(foo) 13 def foo(): 14 print('hello foo') 15 time.sleep(3) 16 17 18 @show_time #bar=show_time(bar) 19 def bar(): 20 print('in the bar') 21 time.sleep(2) 22 23 foo() 24 print('***********') 25 bar()
装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
带参数的被修饰函数
1 import time 2 3 def show_time(func): 4 5 def wrapper(a,b): 6 start_time=time.time() 7 func(a,b) 8 end_time=time.time() 9 print('spend %s'%(end_time-start_time)) 10 11 return wrapper 12 13 @show_time #add=show_time(add) 14 def add(a,b): 15 16 time.sleep(1) 17 print(a+b) 18 19 add(2,4)
装饰器带参数
import time def time_logger(flag=0): def show_time(func): def wrapper(*args,**kwargs): start_time=time.time() func(*args,**kwargs) end_time=time.time() print('spend %s'%(end_time-start_time)) if flag: print('将这个操作的时间记录到日志中') return wrapper return show_time @time_logger(3) # add = time_loger(3)(add) def add(*args,**kwargs): time.sleep(1) sum=0 for i in args: sum+=i print(sum) add(2,7,5)
-------------------------------------------------------
补全
import time
def cal_time(func):
def inter(*args, **kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s time: %s" % (func.__name__, t2 - t1))
return result
return inter
@cal_time
def add(x, y):
return x + y
当使用一个装饰器时,打印函数名会出现问题
print(add.__name__)
##输出
inter
使用标准库里的functools.wraps
,可以基本解决这个问题
看的别人写的文章说是拿不到数字签名等信息,但是在Python3.5里面可以拿到
可以使用wrapt包来进行修正
Wrapt
需要安装
pip install wrapt
wrapt是一个功能非常完善的包,用于实现各种你想到或者你没想到的装饰器。使用wrapt实现的装饰器你不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)
也准确无误。
import wrapt
@wrapt.decorator
def cal_time(func, instance, args, kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s time: %s" % (func.__name__, t2 - t1))
return result
@cal_time
def add(x, y):
return x + y
使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs)
,注意第二个参数instance
是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据instance
的值你能够更加灵活的调整你的装饰器。另外,args
和kwargs
也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。
如果你需要使用wrapt写一个带参数的装饰器,可以这样写。
def logging(level):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print("[{}]: enter {}()".format(level, wrapped.__name__))
return wrapped(*args, **kwargs)
return wrapper
@logging(level="INFO")
def do(work): pass
类装饰器及更多内容
详解Python的装饰器
Python装饰器的另类用法