2014-07-10

Python 的 decorator (Part I)

緣起
當我第一次看到 python 的 @ 符號時,當場傻眼,這是什麼東咚? 這是 command line 的變數的控制符號嗎?
後來去 goo 了一下才發現這東西叫 decorator
花了五分鐘看了一下, 瞭解了
但再實際不同 source 去運作及比對, 才發現...好像怪怪的, 不懂
再去搜集研究比對一下, 好像又懂了
再遇到 wraper 名稱取代 ... 哇~ 又不懂了
一波三折~ 5分鐘的事搞成半小時

Pre-說明
我最討厭只是為了要瞭解一個東西的意義/作用, 就要看人家 blog 寫的落落長的教學文, 寫一堆無關緊要的東西, 但重點只有一個. 甚至還講不到重點(我想即使版主未來想自己參考, 我猜他自己還會看不懂, 真的~)
但 decorator 要說清楚真的需要落落長, 廢話少說開始吧...
我的理解是...
首先, before all
decorator - 字面上意義是裝飾者, 裝飾器, 但這種叫法好像怪怪的(你懂後再想想, 先 pass)

然後你必需先知道一件事, 那就是 python 是可以把 function 當參數放在另一個 function 內執行的, 而且也可以用變數指向一個 function

>>> def func1(n):
...     return n+1
...
>>> func1(9)
10
>>> pfun = func1
>>> pfun(8)
9
>>>



進入主題
以下面程式片段為例
# decorator
def ChrismasTree(func):
    def my_wrap(*args, **kwargs):
        print("pos 1 ....")
        print("pos 2 ....")
        out = func(*args, **kwargs)
        print("pos 3 ....")
        print("my name is: ", func.__name__)
    return my_wrap

# callee
@ChrismasTree
def Star(color):
    # with color do something ...
    print(color)
    return

Star("red")
print(Star.__name__)


@ 符號後面的 ChrismasTree 就是 decorator(裝飾函數), 而 function Star 則是被裝飾的物品(decoration). 什麼意思? 你若去市場買聖誕樹, 是不是可以買到很多不同的樹? 高的, 矮的, 茂密的, 松樹, ... 有一點很奇怪的是, 這棵樹已經決定(設定)好擺裝飾品的位置, 然後, Star 是放到樹上的裝飾品, 雖然你不能決定位置, 但你可以決定放什麼 Star(什麼內容).

換成程式來說, 也就是說ChrismasTree 就是一個已被設計好的程式, 而你傳進去的 Star 程式則是會在固定位置被執行。 上例的意義就是你把 Star 當裝飾品放到 ChrismasTree 樹上去, 位置不能變, 但 Star 你可以任由你寫. (不過, 其實樹也是可以改的, 這裡只是方便說明, 不誤導你, 固定了一個變因). 所以你要 decorator 一棵樹, 首先就是你要瞭解這棵樹長什麼樣子, 有什麼地方可以放裝飾品. (好像在講廢話, 即使你用最簡單的 print function, 你也是要知道它是幹嘛的)。程式碼寫多了就會發程式碼最重要的就是能重複利用,處理人類的問題(應該說這世上的問題) 很多架構就是固定不變的,而內容卻任意變化。題外話,例如我最常用的就是@loginRequired 。寫好一段碼,需要非 anonymous 用戶,只要把 @loginRequired 放上去,什麼都不用管,loginRequired 裡我就寫檢查權限及登入/註冊的東咚,超....離不開它的。

@loginRequired
def DoSomething():
    .....
    ....

上例展開等同以下, 就是我們上面說的, 把function當參數傳進function, 並且回傳一個新的 function.
def Star(color):
    # with color do something ...
    print(color)
    return
Star = ChrismasTree(Star)


我們來執行看看
########## output

pos 1 ....
pos 2 ....
red
pos 3 ....
my name is:  Star
my_wrap


從結果我們可以看到, start 傳到 chrismastree 被執行, 而執行的函數是 my_wrap. my_warp 包覆了 start 並加了某些東西. 也就是說 ChrismasTree 就是你的 model, 而你的 Star 被填在 ChrismasTree 裡, 這有什麼好處呢? 就是你可以....用 Star 來改樹, 雖然樹是固定, 你可以裝飾不同 star讓它變成各式各樣的聖誔樹. 我們常會有重複在用的 framework, 但這個 framework又無法完全滿足各種情況,故我們可以設計一些放置 star的點, 以便未來使用.這就是decorator的用處.

同一顆樹, 你用紅星星點綴就是紅聖誔樹, 藍星星就變成藍聖誔樹了
而且除了別人提供樹以外, 你是不是也可以自行建樹? 也就是我們可以建好一個 model, 然後未來因為不同需要, 我們可以修改 star 以符合需求而不需要動到樹.


但是, 問題來了, 你有沒有發現 在wrappr function 內, "my name is: Star"; 而 Star.__name__  卻變成了 my_wrap 這個問題? 我的 function name這問題...
下回分曉...

Python 的 decorator (Part II)

沒有留言:

張貼留言