今天來討論一下裝飾器。裝飾器是一個很著名的設計模式,經常被用于有切面需求的場景,較為經典的有插入日志、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼并繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。
1. 裝飾器入門
1.1. 需求是怎么來的?
裝飾器的定義很是抽象,我們來看一個小例子。
代碼如下:
def foo():
print 'in foo()'
foo()
這是一個很無聊的函數沒錯。但是突然有一個更無聊的人,我們稱呼他為B君,說我想看看執行這個函數用了多長時間,好吧,那么我們可以這樣做:
代碼如下:
import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start
foo()
很好,功能看起來無懈可擊。可是蛋疼的B君此刻突然不想看這個函數了,他對另一個叫foo2的函數產生了更濃厚的興趣。
怎么辦呢?如果把以上新增加的代碼復制到foo2里,這就犯了大忌了~復制什么的難道不是最討厭了么!而且,如果B君繼續看了其他的函數呢?
1.2. 以不變應萬變,是變也
還記得嗎,函數在Python中是一等公民,那么我們可以考慮重新定義一個函數timeit,將foo的引用傳遞給他,然后在timeit中調用foo并進行計時,這樣,我們就達到了不改動foo定義的目的,而且,不論B君看了多少個函數,我們都不用去修改函數定義了!
代碼如下:
import time
def foo():
print 'in foo()'
def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
timeit(foo)
看起來邏輯上并沒有問題,一切都很美好并且運作正常!……等等,我們似乎修改了調用部分的代碼。原本我們是這樣調用的:foo(),修改以后變成了:timeit(foo)。這樣的話,如果foo在N處都被調用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調用的代碼無法修改這個情況,比如:這個函數是你交給別人使用的。
1.3. 最大限度地少改動!
既然如此,我們就來想想辦法不修改調用的代碼;如果不修改調用代碼,也就意味著調用foo()需要產生調用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個參數……想辦法把參數統一吧!如果timeit(foo)不是直接產生調用效果,而是返回一個與foo參數列表一致的函數的話……就很好辦了,將timeit(foo)的返回值賦值給foo,然后,調用foo()的代碼完全不用修改!
|
新聞熱點
疑難解答