装饰器(Decorators)是Python的一个重要部分。简单地说:他们是修改其它函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic。
一切皆对象
我们可以将一个函数赋值给一个变量
def hi(): print("hihi") a=hi print(a,type(a)) hi() #调用hi函数,需要加()
运行结果
在函数中定义函数
def hi(): print("hihi") def yun(): return "yunyun" print(yun()) hi() #yun()#报错#在hi()函数之外是不能访问的
运行结果
从函数中返回函数
def hi(name="xx"): print(name) def he(): return "xixi" return he #返回he相当于返回he()函数 a=hi() #此时不会调用he(),因为hi()函数中无调用此函数的语句 print(a,type(a)) print(a()) #函数调用需要加()
运行结果
将函数作为参数传给另一个函数
def hi(name="xx"): print(name) def study(a): a() print("我是study") study(hi)
运行结果
第一个装饰器
下面的代码你看的懂?都是上面刚刚提到的知识点。
def aa(bb): print("我是aa哈") def cc(): print("我是cc吆") bb() return "cc" return cc def xixi(): print("我是xixi") m=aa(xixi) #输出:我是aa哈, print(m,type(m)) #m是cc()函数 print(m()) #我是cc吆,我是xixi,cc函数的返回值
运行结果
下面我们使用@来修改上面的代码
def aa(bb): print("我是aa哈") def cc(): print("我是cc吆") bb() return "cc" return cc @aa def xixi(): print("我是xixi") print(xixi()) #等价于aa(xixi)()
运行结果
下面存在一个问题
print(xixi.__name__) #输出cc
这并不是我们想要的!应该输出xixi。这里的函数被cc替代了。它重写了我们函数的名字和注释文档(doctring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是Functools.wraps。我们使用functools.wraps修改上一个例子
from functools import wraps def aa(bb): print("我是aa哈") @wraps(bb) def cc(): print("我是cc吆") bb() return "cc" return cc @aa def xixi(): print("我是xixi") #print(xixi()) #等价于aa(xixi)() print(xixi.__name__)
运行结果
注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
蓝本规范:
from functools import wraps def aa(bb): print("我是aa哈") @wraps(bb) def cc(*args,**kwargs): if not can_run: return "bb方法不运行" return bb(*args,**kwargs) return cc @aa def xixi(): return "我是xixi" can_run=False print(xixi()) #我是aa哈;bb方法不运行; # can_run=True # print(xixi()) #我是aa哈;我是xixi
使用场景
授权(Authorization)
日志(Logging)
带参数的装饰器
来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢?这是因为,当你使用@aa语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!
在函数中嵌入装饰器
from functools import wraps def aa(name="xiaoxiao"): def bb(cc): @wraps(cc) def dd(*args,**kwargs): log=cc.__name__+"被调用" print(log) return cc(*args,**kwargs) return dd return bb # @aa() #注意与无参的对比,这里多了() # def xixi(): # return "我是xixi" @aa(name="dehua") def xixi(): return "我是xixi" print(xixi()) #xixi被调用,我是xixi
运行结果
装饰器类
没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object): def __init__(self, func): self._func = func #print(self._func) def __call__(self): print ('class decorator runing') self._func() print ('class decorator ending') @Foo def bar(): print ('bar') bar()
运行结果
参考:https://www.runoob.com/w3cnote/python-func-decorators.html
https://www.runoob.com/w3cnote/python-func-decorators.html