想理解Python的decorator首先要知道在Python中函数也是一个对象,所以你可以
将函数复制给变量 将函数当做参数 返回一个函数函数在Python中给变量的用法一样也是一等公民,也就是高阶函数(HighOrderFunction)。所有的魔法都是由此而来。
我们想在函数login中输出调试信息,我们可以这样做
deflogin(): print(inlogin) defprintdebug(func): print(enterthelogin) func() print(exitthelogin) printdebug(login)这个方法讨厌的是每次调用login是,都通过printdebug来调用,但毕竟这是可行的。
既然函数可以作为返回值,可以赋值给变量,我们可以让代码优美一点。
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。这种耦合叫内聚:-)。
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这个函数。
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):#addwrappertoreceviedecoratorsparameter defprintdebug(func): def__decorator(user): print(enterthelogin,anddebuglevelis:+str(level))#printdebuglevel func(user) print(exitthelogin) return__decorator returnprintdebug#returnoriginaldecorator @printdebug_level(level=5)#decoratorsparameter,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’)
有时候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
我们可以对一个函数应用多个装饰器,这时我们需要留心的是应用装饰器的顺序对结果会产生。影响比如:
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]
什么情况下装饰器不适用?装饰器不能对函数的一部分应用,只能作用于整个函数。
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);printresult3Reference:
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