python函数装饰器
装饰器(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
浙公网安备 33010602011771号