Python-理解装饰器
文章先由stackoverflow上面的一個(gè)問(wèn)題引起吧,如果使用如下的代碼:
@makebold @makeitalic def say():return "Hello"打印出如下的輸出:
<b><i>Hello<i></b>你會(huì)怎么做?最后給出的答案是:
def makebold(fn):def wrapped():return "<b>" + fn() + "</b>"return wrappeddef makeitalic(fn):def wrapped():return "<i>" + fn() + "</i>"return wrapped@makebold @makeitalic def hello():return "hello world"print hello() ## 返回 <b><i>hello world</i></b>現(xiàn)在我們來(lái)看看如何從一些最基礎(chǔ)的方式來(lái)理解Python的裝飾器。英文討論參考? Here 。
1.1. 需求是怎么來(lái)的?
裝飾器是一個(gè)很著名的設(shè)計(jì)模式,經(jīng)常被用于有切面需求的場(chǎng)景,較為經(jīng)典的有插入日志、性能測(cè)試、事務(wù)處理等。裝飾器是解決這類(lèi)問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量函數(shù)中與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。
裝飾器的定義很是抽象,我們來(lái)看一個(gè)小例子。
這是一個(gè)很無(wú)聊的函數(shù)沒(méi)錯(cuò)。但是突然有一個(gè)更無(wú)聊的人,我們稱(chēng)呼他為B君,說(shuō)我想看看執(zhí)行這個(gè)函數(shù)用了多長(zhǎng)時(shí)間,好吧,那么我們可以這樣做:
import time def foo():start = time.clock()print 'in foo()'end = time.clock()print 'used:', end - startfoo()很好,功能看起來(lái)無(wú)懈可擊??墒堑疤鄣腂君此刻突然不想看這個(gè)函數(shù)了,他對(duì)另一個(gè)叫foo2的函數(shù)產(chǎn)生了更濃厚的興趣。
怎么辦呢?如果把以上新增加的代碼復(fù)制到foo2里,這就犯了大忌了~復(fù)制什么的難道不是最討厭了么!而且,如果B君繼續(xù)看了其他的函數(shù)呢?
1.2. 以不變應(yīng)萬(wàn)變,是變也 ?
還記得嗎,函數(shù)在Python中是一等公民,那么我們可以考慮重新定義一個(gè)函數(shù)timeit,將foo的引用傳遞給他,然后在timeit中調(diào)用foo并進(jìn)行計(jì)時(shí),這樣,我們就達(dá)到了不改動(dòng)foo定義的目的,而且,不論B君看了多少個(gè)函數(shù),我們都不用去修改函數(shù)定義了!
看起來(lái)邏輯上并沒(méi)有問(wèn)題,一切都很美好并且運(yùn)作正常!……等等,我們似乎修改了調(diào)用部分的代碼。原本我們是這樣調(diào)用的:foo(),修改以后變成了:timeit(foo)。這樣的話(huà),如果foo在N處都被調(diào)用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調(diào)用的代碼無(wú)法修改這個(gè)情況,比如:這個(gè)函數(shù)是你交給別人使用的。
1.3. 最大限度地少改動(dòng)! ?
既然如此,我們就來(lái)想想辦法不修改調(diào)用的代碼;如果不修改調(diào)用代碼,也就意味著調(diào)用foo()需要產(chǎn)生調(diào)用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個(gè)參數(shù)……想辦法把參數(shù)統(tǒng)一吧!如果timeit(foo)不是直接產(chǎn)生調(diào)用效果,而是返回一個(gè)與foo參數(shù)列表一致的函數(shù)的話(huà)……就很好辦了,將timeit(foo)的返回值賦值給foo,然后,調(diào)用foo()的代碼完全不用修改!
這樣,一個(gè)簡(jiǎn)易的計(jì)時(shí)器就做好了!我們只需要在定義foo以后調(diào)用foo之前,加上foo = timeit(foo),就可以達(dá)到計(jì)時(shí)的目的,這也就是裝飾器的概念,看起來(lái)像是foo被timeit裝飾了。在在這個(gè)例子中,函數(shù)進(jìn)入和退出時(shí)需要計(jì)時(shí),這被稱(chēng)為一個(gè)橫切面(Aspect),這種編程方式被稱(chēng)為面向切面的編程(Aspect-Oriented Programming)。與傳統(tǒng)編程習(xí)慣的從上往下執(zhí)行方式相比較而言,像是在函數(shù)執(zhí)行的流程中橫向地插入了一段邏輯。在特定的業(yè)務(wù)領(lǐng)域里,能減少大量重復(fù)代碼。面向切面編程還有相當(dāng)多的術(shù)語(yǔ),這里就不多做介紹,感興趣的話(huà)可以去找找相關(guān)的資料。
這個(gè)例子僅用于演示,并沒(méi)有考慮foo帶有參數(shù)和有返回值的情況,完善它的重任就交給你了 :)
上面這段代碼看起來(lái)似乎已經(jīng)不能再精簡(jiǎn)了,Python于是提供了一個(gè)語(yǔ)法糖來(lái)降低字符輸入量。
重點(diǎn)關(guān)注第11行的@timeit,在定義上加上這一行與另外寫(xiě)foo = timeit(foo)完全等價(jià),千萬(wàn)不要以為@有另外的魔力。除了字符輸入少了一些,還有一個(gè)額外的好處:這樣看上去更有裝飾器的感覺(jué)。
1.4 最后回答前面提到的問(wèn)題:
2、裝飾器的種類(lèi)
2.1 無(wú)參數(shù)裝飾器
第一個(gè)函數(shù)deco是裝飾函數(shù),它的參數(shù)就是被裝飾的函數(shù)對(duì)象。我們可以在deco函數(shù)內(nèi)對(duì)傳入的函數(shù)對(duì)象做一番“裝飾”,然后返回這個(gè)對(duì)象(記住一定要返回 ,不然外面調(diào)用foo的地方將會(huì)無(wú)函數(shù)可用。實(shí)際上此時(shí)foo=deco(foo))
我寫(xiě)了個(gè)小例子,檢查函數(shù)有沒(méi)有說(shuō)明文檔:
2.2 有參數(shù)裝飾器
def decomaker(arg):'通常對(duì)arg會(huì)有一定的要求'"""由于有參數(shù)的decorator函數(shù)在調(diào)用時(shí)只會(huì)使用應(yīng)用時(shí)的參數(shù)而不接收被裝飾的函數(shù)做為參數(shù),所以必須在其內(nèi)部再創(chuàng)建一個(gè)函數(shù)"""def newDeco(func): #定義一個(gè)新的decorator函數(shù)print func, argreturn funcreturn newDeco @decomaker(deco_args) def foo():pass foo()第一個(gè)函數(shù)decomaker是裝飾函數(shù),它的參數(shù)是用來(lái)加強(qiáng)“加強(qiáng)裝飾”的。由于此函數(shù)并非被裝飾的函數(shù)對(duì)象,所以在內(nèi)部必須至少創(chuàng)建一個(gè)接受被裝飾函數(shù)的函數(shù),然后返回這個(gè)對(duì)象(實(shí)際上此時(shí)foo=decomaker(arg)(foo))
這個(gè)我還真想不出什么好例子,還是見(jiàn)識(shí)少啊,只好借用同步鎖的例子了:
調(diào)用時(shí)還是updae(data)。
同時(shí)還可以將多個(gè)裝飾器組合 使用,注意調(diào)用順序:
2.3 內(nèi)置的裝飾器
內(nèi)置的裝飾器有三個(gè),分別是staticmethod、classmethod和property,作用分別是把類(lèi)中定義的實(shí)例方法變成靜態(tài)方法、類(lèi)方法和類(lèi)屬性。由于模塊里可以定義函數(shù),所以靜態(tài)方法和類(lèi)方法的用處并不是太多,除非你想要完全的面向?qū)ο缶幊?。而屬性也不是不可或缺?#xff0c;Java沒(méi)有屬性也一樣活得很滋潤(rùn)。從我個(gè)人的Python經(jīng)驗(yàn)來(lái)看,我沒(méi)有使用過(guò)property,使用staticmethod和classmethod的頻率也非常低。
2.4 裝飾器進(jìn)階
http://www.cnblogs.com/JohnABC/p/4186209.html
具體請(qǐng)參考: http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
轉(zhuǎn)自: http://www.open-open.com/lib/view/open1374584644558.html
轉(zhuǎn)載于:https://www.cnblogs.com/JohnABC/p/4091532.html
總結(jié)
以上是生活随笔為你收集整理的Python-理解装饰器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: operation 多线程
- 下一篇: CentOS升级Python2.7及安装