decorator和closure想必都是用過python的大家聽過的名詞,但是應該有些人還對他們霧煞煞,也不了解他們之間的關係可親密的勒,可以說若沒有closure的概念也不會有decorator這個難懂的東西存在,因為每個decorator都是closure這個概念的一個實現。在講一些看似深奧的道理之前,我們先來說明一下decorator的存在意義。
decorator意思很簡單,就是裝飾,裝飾python中的class和function,也就是為他們多加一些內容。
而這些多加的內容是為了應付不靈光的人腦用的,比如說寫log、計算時間或是做一些測試或檢查,通常這些內容大同小異,而且跟function本身的功能沒有什麼太大的關係,你想想,若要對一些function作檢查或是效能測試,就要對這些function"暫時"加入幾行命令,用完後還要給他註解掉,這不是超麻煩嗎?所以decorator的誕生就是要來解決這個問題的,千萬不要怕他,一定要喜歡用他。
舉個例子好了,先搬出前幾天在版本差異雜談之五寫的code:
import time
ts = time.time()
for i in range(10000000):
a = i
te = time.time()
print ("range's time consume: %f" % (te-ts))
ts = time.time()
for i in xrange(10000000):
a = i
te = time.time()
print ("xrange's time consume: %f" % (te-ts))
這是用來比較range和xrange的效能的程式,有沒有看到為了計算兩個for迴圈所用的時間,我必須重複的複製一樣的指令:
ts = time.time()
te = time.time()
print ("xrange's time consume: %f" % (te-ts))
若我們想同時比較多個程序的速度,每個程序都必須複製這三行,實在是非常麻煩,但現在有了decorator,我們的負擔就會減輕許多:
import time
def time_count_wrapper(func):
def time_count():
ts = time.time()
func()
te = time.time()
print ("time consume: %f" % (te-ts))
return time_count
@time_count_wrapper
def test_range():
for i in range(10000000):
a = i
@time_count_wrapper
def test_xrange():
for i in xrange(10000000):
a = i
test_range()
test_xrange()
這段code也同樣的比較了range和xrange的迭代時間,雖然在兩個for迴圈之外我們多定義了一個time_count_wrapper(這個函數就是我們所謂的decorator),但當我們想要比較多個程序的處理時間,我們只要在這些function前面多加'@time_count_wrapper',time_count_wrapper就會加些計算處理時間的'裝飾'在我們function上面,從上個範例的每個程序複製三行甚至更多行,縮減到現在每個程序只要複製一行,省去了很多人工複製的時間,而且程式碼的可讀性也變高了,從每個function前面的裝飾語法就可以看出我們想對這些function執行什麼測試。
這就是一個最簡單的裝飾器使用範例了,接著我們來探討一下裝飾器的內部結構,可以看到上個範例的time_count_wrapper的內部竟然還能巢狀定義一個函數,並以此拿來作為回傳值,好像整個程式都因此染上了深不可測的黑科技氛圍,但若了解到函式在python裡也被視做一個對象,也就是說他是一個實際佔有記憶體的一個實體,可被創造與傳遞,那好像對於他能夠在哪裡定義並怎麼傳遞都不是那麼神奇了。
在動態型別系列二其實也有提到'對象'的概念,只是在那個時候我是用物件(object)來稱呼,還是用大家慣常的詞來正名一下好了,那個時候有提到python有分可變對象以及不可變對象,而我們現在提及的function對象是一個不可變對象,在python中定義一個function,就是創造一個不可變的function對象:
In python3:
>>> def foo(): # 創建一個函數對象,並用變數foo指向他
... print('lalala')
...
>>> id(foo)
140478323559416
>>> type(foo)
<class 'function'>
>>> def bar(): # 創建一個函數對象,並用變數bar指向他
... print('lalala')
...
>>> id(bar)
140478323216168
>>> foo = bar # 將變數foo指向bar所指向的對象
>>> id(foo)
140478323216168
>>>
今天先這樣,掰掰,明天繼續講解closure的概念。