python之装饰器详解
這幾天翻看python語法,看到裝飾器這里著實卡了一陣,最初認為也就是個函數(shù)指針的用法,但仔細研究后發(fā)現(xiàn),不止這么簡單。
首先很多資料將裝飾器定義為AOP的范疇,也就是Aspect Oriented Programming面向切面編程的概念,不懂AOP不要緊,只要有函數(shù)指針的概念,又有嵌套函數(shù)的基礎知識,看懂此文一點壓力都沒有。
先說說為什么要有裝飾器這么個東西存在吧,這是一種設計模式,較為經(jīng)典的有插入日志、性能測試、事務處理等等。概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能。
關于嵌套函數(shù),C/C++程序員可能會覺得一頭霧水,因為譚老爺子的書明確指出函數(shù)不可以嵌套定義,在C++ primer也僅僅有嵌套類的講解,并不存在嵌套函數(shù)一說,但是在一些高級語言中,比如Object C,swift都存在嵌套函數(shù)主題,感興趣的同學可以去翻一下,尤其是當下熱門swift,嵌套函數(shù)和閉包密不可分,可以方便的實現(xiàn)很多邏輯,比如返回一個函數(shù)給調用者。
書歸正傳,所謂裝飾器,顧名思義,就是用來裝飾的神器,你有一個函數(shù)A(),覺得它不夠漂亮,但是又不想重寫它,咋辦呢?裝飾器(Decorator)這時候就派上用場了。
為了方便大家的理解,我先寫一個簡單的嵌套函數(shù):
def funcA():print("i am funcA()!")def funcB():print("i am funcB()")return funcB callSth=funcA()//調用funcA得到返回的funcB callSth()//相當于調用funcB?
值得說明一點,在python里只要你不怕蛋疼,你可以任意嵌套,哪怕你嵌套個百十層直到棧溢出都沒問題。
如下:
def ppp():print("return vlaue") def first():a=10// a被嵌套內的函數(shù)捕獲!print("the first layer")def second():print("the second layer %d" % a)def third():print("the third layer %d " % a)def forth():print("the forth layer")return ppp()return forthreturn thirdreturn second aaa=first()()()//得到forth() aaa()?關于嵌套函數(shù)是這樣執(zhí)行的,先執(zhí)行最外層的first(),返回second然后發(fā)現(xiàn)后面跟著(),那繼續(xù)返回third,以此類推,聰明如你,肯定明白了。嵌套函數(shù)就這樣,簡單到爆。如果到這里諸君理解了,裝飾器基本上也就理解了。
下面正式開始裝飾器的概念(開啟摘抄模式):
裝飾器的定義很是抽象,我們來看一個小例子。
def foo():print 'in foo()'foo()
?
這是一個很無聊的函數(shù)沒錯。但是突然有一個更無聊的人,我們稱呼他為B君,說我想看看執(zhí)行這個函數(shù)用了多長時間,好吧,那么我們可以這樣做:
import time def foo():start = time.clock()print 'in foo()'end = time.clock()print 'used:', end - startfoo()?
很好,功能看起來無懈可擊。可是蛋疼的B君此刻突然不想看這個函數(shù)了,他對另一個叫foo2的函數(shù)產(chǎn)生了更濃厚的興趣。
怎么辦呢?如果把以上新增加的代碼復制到foo2里,這就犯了大忌了~復制什么的難道不是最討厭了么!而且,如果B君繼續(xù)看了其他的函數(shù)呢?
?
以不變應萬變:還記得嗎,函數(shù)在Python中是一等公民,那么我們可以考慮重新定義一個函數(shù)timeit,將foo的引用傳遞給他,然后在timeit中調用foo并進行計時,這樣,我們就達到了不改動foo定義的目的,而且,不論B君看了多少個函數(shù),我們都不用去修改函數(shù)定義了!
import timedef foo():print 'in foo()'def timeit(func):start = time.clock()func()end =time.clock()print 'used:', end - starttimeit(foo)看起來邏輯上并沒有問題,一切都很美好并且運作正常!……等等,我們似乎修改了調用部分的代碼。原本我們是這樣調用的:foo(),修改以后變成了:timeit(foo)。這樣的話,如果foo在N處都被調用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調用的代碼無法修改這個情況,比如:這個函數(shù)是你交給別人使用的。
最大限度地少改動!
既然如此,我們就來想想辦法不修改調用的代碼;如果不修改調用代碼,也就意味著調用foo()需要產(chǎn)生調用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個參數(shù)……想辦法把參數(shù)統(tǒng)一吧!如果timeit(foo)不是直接產(chǎn)生調用效果,而是返回一個與foo參數(shù)列表一致的函數(shù)的話……就很好辦了,將timeit(foo)的返回值賦值給foo,然后,調用foo()的代碼完全不用修改!
一個真正意義上的裝飾器誕生了:
#-*- coding: UTF-8 -*- import timedef foo():print 'in foo()'# 定義一個計時器,傳入一個,并返回另一個附加了計時功能的方法 def timeit(func):# 定義一個內嵌的包裝函數(shù),給傳入的函數(shù)加上計時功能的包裝def wrapper():start = time.clock()func()end =time.clock()print 'used:', end - start# 將包裝后的函數(shù)返回return wrapperfoo = timeit(foo) foo()?
樣,一個簡易的計時器就做好了!我們只需要在定義foo以后調用foo之前,加上foo = timeit(foo),就可以達到計時的目的,這也就是裝飾器的概念,看起來像是foo被timeit裝飾了。在在這個例子中,函數(shù)進入和退出時需要計時,這被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。與傳統(tǒng)編程習慣的從上往下執(zhí)行方式相比較而言,像是在函數(shù)執(zhí)行的流程中橫向地插入了一段邏輯。在特定的業(yè)務領域里,能減少大量重復代碼。面向切面編程還有相當多的術語,這里就不多做介紹,感興趣的話可以去找找相關的資料。
?
語法糖
上面這段代碼看起來似乎已經(jīng)不能再精簡了,Python于是提供了一個語法糖來降低字符輸入量。
import timedef timeit(func):def wrapper():start = time.clock()func()end =time.clock()print 'used:', end - startreturn wrapper@timeit// syntax sugar 是也,在定義上加上這一行與另外寫foo = timeit(foo)完全等價,千萬不要以為@有另外的魔力。除了字符輸入少了一些,還有一個額外的好處:這樣看上去更有裝飾器的感覺。 def foo():print 'in foo()'foo()?給出原文鏈接:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
相關資料:http://blog.csdn.net/thy38/article/details/4471421
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
?
轉載于:https://www.cnblogs.com/wangjunyan/p/5046223.html
總結
以上是生活随笔為你收集整理的python之装饰器详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux版小米随身WIFI驱动,在官网
- 下一篇: python json解析工具选择_推荐