Python中的装饰器(decorator)
本文内容纲要:
-1,起源
-2,让代码变得优美一点
-3,让代码再优美一点
-4,加上参数
-5,装饰有返回值的函数
-6,应用多个装饰器
-7,灵活运用
想理解Python的decorator首先要知道在Python中函数也是一个对象,所以你可以
- 将函数复制给变量
- 将函数当做参数
- 返回一个函数
函数在Python中给变量的用法一样也是一等公民,也就是高阶函数(HighOrderFunction)。所有的魔法都是由此而来。
1,起源
我们想在函数login中输出调试信息,我们可以这样做
deflogin():
print('inlogin')
defprintdebug(func):
print('enterthelogin')
func()
print('exitthelogin')
printdebug(login)
这个方法讨厌的是每次调用login是,都通过printdebug来调用,但毕竟这是可行的。
2,让代码变得优美一点
既然函数可以作为返回值,可以赋值给变量,我们可以让代码优美一点。
deflogin():
print('inlogin')
defprintdebug(func):
def__decorator():
print('enterthelogin')
func()
print('exitthelogin')
return__decorator#functionasreturnvalue
debug_login=printdebug(login)#functionassigntovariable
debug_login()#executethereturnedfunction
这样我们每次只要调用debug_login就可以了,这个名字更符合直觉。我们将原先的两个函数printdebug和login绑定到一起,成为debug_login。这种耦合叫内聚:-)。
3,让代码再优美一点
printdebug和login是通过debug_login=printdebug(login)这一句来结合的,这一句似乎也是多余的,能不能在定义login是加个标注,从而将printdebug和login结合起来?
上面的代码从语句组织的角度来讲很难再优美了,Python的解决方案是提供一个语法糖(SyntaxSugar),用一个@符号来结合它们。
defprintdebug(func):
def__decorator():
print('enterthelogin')
func()
print('exitthelogin')
return__decorator
@printdebug#combinetheprintdebugandlogin
deflogin():
print('inlogin')
login()#makethecallingpointmoreintuitive
可以看出decorator就是一个:使用函数作参数并且返回函数的函数。通过改进我们可以得到:
- 更简短的代码,将结合点放在函数定义时
- 不改变原函数的函数名
在Python解释器发现login调用时,他会将login转换为printdebug(login)()。也就是说真正执行的是__decorator这个函数。
4,加上参数
1,login函数带参数
login函数可能有参数,比如login的时候传人user的信息。也就是说,我们要这样调用login:
login(user)
Python会将login的参数直接传给__decorator这个函数。我们可以直接在__decorator中使用user变量:
defprintdebug(func):
def__decorator(user):#addparameterreceivetheuserinformation
print('enterthelogin')
func(user)#passusertologin
print('exitthelogin')
return__decorator
@printdebug
deflogin(user):
print('inlogin:'+user)
login('jatsz')#arguments:jatsz
我们来解释一下login(‘jatsz’)的调用过程:
[decorated]login(‘jatsz’)=>printdebug(login)(‘jatsz’)=>__decorator(‘jatsz’)=>[real]login(‘jatsz’)
2,装饰器本身有参数
我们在定义decorator时,也可以带入参数,比如我们这样使用decorator,我们传入一个参数来指定debuglevel。
@printdebug(level=5)
deflogin
pass
为了给接收decorator传来的参数,我们在原本的decorator上在包装一个函数来接收参数:
defprintdebug_level(level):#addwrappertoreceviedecorator'sparameter
defprintdebug(func):
def__decorator(user):
print('enterthelogin,anddebuglevelis:'+str(level))#printdebuglevel
func(user)
print('exitthelogin')
return__decorator
returnprintdebug#returnoriginaldecorator
@printdebug_level(level=5)#decorator'sparameter,debuglevelsetto5
deflogin(user):
print('inlogin:'+user)
login('jatsz')
我们再来解释一下login(‘jatsz’)整个调用过程:
[decorated]login(‘jatsz’)=>printdebug_level(5)=>printdebug[withclosurevalue5](login)(‘jatsz’)=>__decorator(‘jatsz’)[usevalue5]=>[real]login(‘jatsz’)
5,装饰有返回值的函数
有时候login会有返回值,比如返回message来表明login是否成功。
login_result=login(‘jatsz’)
我们需要将返回值在decorator和调用函数间传递:
defprintdebug(func):
def__decorator(user):
print('enterthelogin')
result=func(user)#receviethenativefunctioncallresult
print('exitthelogin')
returnresult#returntocaller
return__decorator
@printdebug
deflogin(user):
print('inlogin:'+user)
msg="success"ifuser=="jatsz"else"fail"
returnmsg#loginwithareturnvalue
result1=login('jatsz');
printresult1#printloginresult
result2=login('candy');
printresult2
我们解释一下返回值的传递过程:
...omitforbrief…[real][msgfromlogin(‘jatsz’)=>[resultfrom]__decorator=>[assignto]result1
6,应用多个装饰器
我们可以对一个函数应用多个装饰器,这时我们需要留心的是应用装饰器的顺序对结果会产生。影响比如:
defprintdebug(func):
def__decorator():
print('enterthelogin')
func()
print('exitthelogin')
return__decorator
defothers(func):#defineaotherdecorator
def__decorator():
print'***otherdecorator***'
func()
return__decorator
@others#applytwoofdecorator
@printdebug
deflogin():
print('inlogin:')
@printdebug#switchdecoratororder
@others
deflogout():
print('inlogout:')
login()
print('---------------------------')
logout()
我们定义了另一个装饰器others,然后我们对login函数和logout函数分别应用这两个装饰器。应用方式很简单,在函数定义是直接用两个@@就可以了。我们看一下上面代码的输出:
$pythondeoc.py
***otherdecorator***
enterthelogin
inlogin:
exitthelogin
---------------------------
enterthelogin
***otherdecorator***
inlogout:
exitthelogin
我们看到两个装饰器都已经成功应用上去了,不过输出却不相同。造成这个输出不同的原因是我们应用装饰器的顺序不同。回头看看我们login的定义,我们是先应用others,然后才是printdebug。而logout函数真好相反,发生了什么?如果你仔细看logout函数的输出结果,可以看到装饰器的递归。从输出可以看出:logout函数先应用printdebug,打印出“enterthelogin”。printdebug的__decorator调用中间应用了others的__decorator,打印出“***otherdecorator***”。其实在逻辑上我们可以将logout函数应用装饰器的过程这样看(伪代码):
@printdebug#switchdecoratororder
(
@others
(
deflogout():
print('inlogout:')
)
)
我们解释一下整个递归应用decorator的过程:
[printdebugdecorated]logout()=>
printdebug.__decorator[call[othersdecorated]logout()]=>
printdebug.__decorator.other.__decorator[callreallogout]
7,灵活运用
什么情况下装饰器不适用?装饰器不能对函数的一部分应用,只能作用于整个函数。
login函数是一个整体,当我们想对部分函数应用装饰器时,装饰器变的无从下手。比如我们想对下面这行语句应用装饰器:
msg="success"ifuser=="jatsz"else"fail"
怎么办?
一个变通的办法是“提取函数”,我们将这行语句提取成函数,然后对提取出来的函数应用装饰器:
defprintdebug(func):
def__decorator(user):
print('enterthelogin')
result=func(user)
print('exitthelogin')
returnresult
return__decorator
deflogin(user):
print('inlogin:'+user)
msg=validate(user)#exacttoamethod
returnmsg
@printdebug#applythedecoratorforexactedmethod
defvalidate(user):
msg="success"ifuser=="jatsz"else"fail"
returnmsg
result1=login('jatsz');
printresult1
来个更加真实的应用,有时候validate是个耗时的过程。为了提高应用的性能,我们会将validate的结果cache一段时间(30seconds),借助decorator和上面的方法,我们可以这样实现:
importtime
dictcache={}
defcache(func):
def__decorator(user):
now=time.time()
if(userindictcache):
result,cache_time=dictcache[user]
if(now-cache_time)>30:#cacheexpired
result=func(user)
dictcache[user]=(result,now)#cachetheresultbyuser
else:
print('cachehits')
else:
result=func(user)
dictcache[user]=(result,now)
returnresult
return__decorator
deflogin(user):
print('inlogin:'+user)
msg=validate(user)
returnmsg
@cache#applythecacheforthisslowvalidation
defvalidate(user):
time.sleep(5)#simulate10secondblock
msg="success"ifuser=="jatsz"else"fail"
returnmsg
result1=login('jatsz');printresult1
result2=login('jatsz');printresult2#thisloginwillreturnimmediatelybyhitthecache
result3=login('candy');printresult3
Reference:
http://stackoverflow.com/questions/739654/understanding-python-decorators--UnderstandingPythondecorators
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html--Python装饰器学习(九步入门)
http://www.python.org/dev/peps/pep-0318/--PEP318--DecoratorsforFunctionsandMethods
本文内容总结:1,起源,2,让代码变得优美一点,3,让代码再优美一点,4,加上参数,5,装饰有返回值的函数,6,应用多个装饰器,7,灵活运用,
原文链接:https://www.cnblogs.com/Jerry-Chou/archive/2012/05/23/python-decorator-explain.html