在約莫一兩個月前,我被賦予一個任務是利用Python Decorator做重構
當時我還不知道使用Decorator來解決問題,可以砍掉專案一半的代碼
Decorator是一個超棒的方法,但對於我來說它並不太好學習。
初次嘗試Decorator 是在今年的1月,我用了剛跑完Scurm 一個 Spring結束的喘息時間,嘗試學習Decorator,目的就是達成前輩開的最後一個Issue: 用Decorator重構專案。
我有看過前輩使用Decorator, 就像還在學日文50音的程度看著前輩們寫出一篇日文的文章,你看著前輩那些高超的程式碼,竟懷疑起自己到底能不能達到那個境界…
我東找找西找找就是沒辦法好好地寫出一個至少能動的decorator
而這篇,就是寫紀錄,我成功學習了Decorator來重構專案程式碼,並刪除至少一半的code
什麼是Decorator
Decorator就是裝飾器, 如下列程式碼,很像 裝飾
在函式上的裝飾器
@menu
def make_coffee():
pass
@menu
def make_ice_tea():
pass
在這個範例中 @menu
就是Decorator,我簡單用做餐點的邏輯寫一個範例程式
我學會了Decorator並運用在專案,這讓我砍掉我先前寫的一半的程式碼,但在文章中我不會秀出我在專案寫的程式碼,會用很相同的邏輯改寫。
前提有提及在剛開始找遍各大網站就是看不懂Decorator怎麼使用qq,但在功力提升的兩個月後,搭配找到的寶典,終於實作出我要的功能啦!!!
寶典在此,感謝大大的整理
來實作Decorator囉!
這個實作參考 有參數的 Decorator Function
再次強調這個參考實作來自OT Coding Note,有興趣可以點進去看
Note: 需要多一層的 outter function 來傳入參數
def decorateFruit(fruit, rotLevel):
def outer_d_f(f):
def d_f(*args, **kargs):
print("%s %s before call" % (rotLevel, fruit))
result = f(*args, **kargs)
print("%s %s after call" % (rotLevel, fruit))
return result
return d_f
return outer_d_f
@decorateFruit('banana', 'new')
def print_hello2():
print("hello 2nd time.")
@decorateFruit('guava', '50% rot')
def print_hello3():
print("hello 3th time.")
> print_hello2()
> print('')
> print_hello3()
output
new banana before call
hello 2nd time.
new banana after call
50% rot guava before call
hello 3th time.
50% rot guava after call
改成自己想實現的功能
首先先介紹一下需求:
我在專案裡主要是運用來判斷眾資料庫欄位的 資料型態
: object, float64 ..
所以如果有相關類似需求也可以拿來改寫!但由於是文章中就必須保密做改寫惹qq
需求描述:
假設我們開一個飲料店,做不同的飲料需要不同的材料(material)跟工具(tools)
每個飲料都會制定用哪些材料製作,製作的時候會檢查材料倉庫(storehouse)
若沒有足夠的材料可以製作飲料就不做,我要怎麼達成自動判斷呢?
我寫了一個範例如下,是改裝OT Coding Note 的 有參數的 Decorator Function
# 這是我的Decorator
def auto_check_material(material):
def outer_func(make):
def check_material(*args):
# 假設 storehouse 是list, 內有倉庫全部有的做菜材料
storehouse, tools = args
# 檢查倉庫裡有沒有材料
if material not in storehouse:
# 倉庫裡沒材料所以 False 不做
return False
# 反之倉庫有材料, 我們就傳入製作飲料的function
return make(*args)
return check_material
return outer_func
# 裝飾Decorator的函式們, 會輸入製作這杯飲料要具備什麼材料
@auto_check_material(['coffee beans', 'water'])
def make_coffee(food_storehouse, tools):
# 倉庫有材料才會做
pass
@auto_check_material(['black tea bag', 'water'])
def make_ice_tea(food_storehouse, tools):
# 倉庫有材料才會做
pass
@auto_check_material(['black tea bag', 'apple', 'water'])
def make_apple_tea(food_storehouse, tools):
# 倉庫有材料才會做
pass
有些註解寫好了
我的Decorator 是 auto_check_material
, 這個函式主要幫我自動確認倉庫內有無材料,傳入的變數為 製作某個飲料需要的材料
。
會有三層 function (但我現在還沒深入研究qq, 所以若講解怪怪的要自動訂正XD)
合理假設 outer_func(make)
就是被裝飾的函式們, 也就是說我在 check_material
確認材料有在倉庫後就會執行 return make(*args)
,確認可以製作飲料,就進到製作的函式開始製作飲料。
***講解時間***
我也是在打文章時才梳理清楚邏輯XD, 也就是說這些函式的執行是
當程式一執行,最先會經過三個製作飲料的函式
假設一開始run 遇到 make_coffee
@auto_check_material(['coffee beans', 'water'])
def make_coffee(food_storehouse, tools):
pass
接著發現製作咖啡 make_coffee 的函式上有個裝飾器 auto_check_material
, 代表前提是要先通過auto_check_material
合格後才能執行 make_coffee
。
裝飾器上帶有 (['coffee beans', 'water'])
,這是執行Decorator 需要用到的變數,在需求中代表著要有這兩個材料才能做咖啡。
進到 Decorator
# 這是我的Decorator
def auto_check_material(material):
def outer_func(make):
# 會先經過並執行check_material
def check_material(*args):
# 假設 storehouse 是list, 內有倉庫全部有的做菜材料
storehouse, tools = args
if material not in storehouse:
return False
# 若符合就進到outer_func也就是外部的 def make_coffee():
return make(*args)
return check_material
return outer_func
因為進到裝飾器內,所以這邊的 outer_func
指的是在外層製作飲料的函式 make_coffee
意思是說在這個裝飾器內,前提要先通過 check_material
確認材料都有在 storehouse
內,才將變數 *args
塞往下一步 > outer_func
外部函式 (也就是咱製作飲料的函式)
@auto_check_material(['coffee beans', 'water'])
def make_coffee(storehouse, tools):
# 執行玩decorator後, 符合True 就做這個function
# 變數 storehouse, tools 會一起送進Decorator 再回來這個函式中使用
...
# 一連串製作後,生出咖啡,送出咖啡
return coffee
執行完 Decorator » outer_func » 執行make_coffee 製作咖啡
總結順序
這邊釐清一下執行函式的順序
程式跑下去的順序依序是
>> def make_coffee >> @auto_check_material >>
check_material(*args) >> outer_func(make) >> def make_coffee(製作)
>> def make_ice_tea >> @auto_check_material >>
check_material(*args) >> outer_func(make) >> def make_ice_tea(製作)
>> def make_apple_tea >> @auto_check_material >>
check_material(*args) >> outer_func(make) >> def make_apple_tea(製作)
嗯!大概是這樣子
想當初, 真心覺得實作Decorator困難,因為是用上班時間學習 + 實作於專案
只要一整個下午都在研究這個,就覺得天啊我好像花了很多時間qq
但後來因為專案進度又開始跑了,才在當時學一半就沒繼續往下學
經過兩個月程式能力有稍微變強(?),也許是找到神人的文章XD
才順利地利用Decorator 重構專案程式!!! 真心超級讚啊qqqqqq
這也代表了我的程式能力又精進了一大步呢!!