装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。装饰器(decorator) 是一个返回函数的高阶函数(把函数作为参数传入,这样的函数称为高阶函数),装饰器在Python中如此方便都要归因于Python的函数可以像普通的函数一样能作为参数。传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内,函数进入和退出时 ,被称为一个横切面,这种编程方式被称为面向切面的编程。在java中也有这个概念。
例如:
@user_logging
def log():
print("log")
上面的过程简单的来说就类似于bar = user_logging(bar),就相当于把bar放入user_logging中处理一番,再返回赋值给bar,这样bar就被user_logging装饰了。@其实就是python中提供的语法糖,我们不需要每次都采用bar = user_logging(bar)这种方式,直接使用@就可以了。
普通的装饰器
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper @log def now(): print('2018-1-21')
结果如下:
now()
call now():
2018-1-21
带参数的装饰器
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator @log('execute') def now(): print('2018-01-21')
结果如下:
now()
execute now():
2018-01-21
使用装饰器极大的复用了代码。但是他有一个缺点就是原函数的元信息不见了,主要函数被装饰后一些属性变为装饰器的属性了,比如__name__ __doc__等等。好在我们有functools.wraps这个装饰器,他能把原函数的元信息拷贝到装饰器函数中 如果有多层嵌套 那么 functools.wraps 应该加在最里面一个装饰器函数前面。
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
装饰器调用顺序 装饰器是可以叠加使用的,那么就存在一个调用先后顺序,这里面是deco_2先调用再调用deco_1
例如:
@deco_1
@deco_2
def addFunc(a,b):
print("ccd")
类装饰器:
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。如果希望让一个类变成装饰器,就需要定义它的__call__方法,因为这里进行类似于这样的处理:bar= Foo(bar),所以需要将类变为可调用的,需要定义
它的__call__方法,对于类x提供了__call__,则x(arg1,arg2,..)就等同于x.__call__(arg1,arg2,....)
class Foo(object): def __init__(self, func): self._func = func def __call__(self): print ('class decorator runing') self._func() print ('class decorator ending') @Foo def bar(): print ('bar')
bar()
结果:
class decorator runing bar class decorator ending