程式扎記: [Python 學習筆記] 進階議題 : 修飾器 (函式修飾器)

標籤

2015年11月29日 星期日

[Python 學習筆記] 進階議題 : 修飾器 (函式修飾器)

轉載自 這裡 
前言 : 
你設計了一個點餐程式,目前主餐有炸雞,價格為49元 : 
  1. def friedChicken():  
  2.     return 49.0  
  3.   
  4. print(friedChicken()) # 49.0  
之後在幾個地方都 呼叫了 friedChicken() 函式來計算餐點價格,現在你打算增加附餐,但又不想直接修改 friedChicken() 函式,也不想另外增加一個 friedChickenside1() 函式,然後到處修改先前使用到 friedchicken() 函式的地方,則你可以這麼撰寫 : 
  1. def friedChicken():  
  2.     return 49.0  
  3.   
  4. print('Fried Chicken=', friedChicken()) # 49.0  
  5.   
  6. def sideDish1(meal):  
  7.     return lambda: meal() + 30  
  8.   
  9. friedChicken = sideDish1(friedChicken)  
  10. print('Fried Chicken Dish=',friedChicken()) # 79.0  
sideDish1() 接受函式物件,函式中使用 lambda 建立一個函式物件,該函式物件執行傳入的函式取得主餐價格,再加上附餐價格,sideDish1() 傳回所建立的函式物件給friedchicken參考,所以之後執行的 friedChicken(),就會是主餐加附餐的價格. 

函式修飾器 : 
以上是傳遞函式的一個應用. 在 Python 中,你還可以使用以下的語法 : 
- test2.py : 
  1. def sideDish1(meal):  
  2.     return lambda: meal() + 30  
  3.   
  4. @sideDish1  
  5. def friedChicken():  
  6.     return 49.0  
  7.   
  8. print('Fried Chicken Dish=', friedChicken()) # 79  
@ 之後所接上的名稱,實際上就是個函式@sideDish1 這樣的標注方式,讓 @sideDish1 更像是個修飾器(Decorator),將 friedChicken() 函式加以修飾,增加附餐價格! 

你可以堆疊修飾器,例如 : 
- test3.py : 
  1. def sideDish1(meal):  
  2.     return lambda: meal() + 30  
  3.   
  4. def sideDish2(meal):  
  5.     return lambda: meal() + 40  
  6.   
  7. @sideDish2  
  8. @sideDish1  
  9. def friedChicken():  
  10.     return 49.0  
  11.   
  12. print(friedChicken()) # 119.0  
實際上, 上面代碼等同 : 
  1. def sideDish1(meal):  
  2.     return lambda: meal() + 30  
  3.       
  4. def sideDish2(meal):  
  5.     return lambda: meal() + 40  
  6.   
  7. def friedChicken():  
  8.     return 49.0  
  9.   
  10. friedchicken = sideDish2(sideDish1(friedChicken))  
  11.   
  12. print(friedchicken())   # 119.0  
如果你的修飾器語法需要帶有參數,則記得,會先以參數執行一次修飾器,傳回函式物件再修飾指定的函式. 例如 : 
  1. @deco('param')  
  2. def func():  
  3.     pass  
實際上等於 : 
  1. func = deco('param')(func)  
所以若要讓點餐程式更有彈性一些,你可以這麼設計 : 
- test4.py : 
  1. def sideDish(number):  
  2.     return {  
  3.         1 : lambda meal: (lambda: meal() + 30),  
  4.         2 : lambda meal: (lambda: meal() + 40),  
  5.         3 : lambda meal: (lambda: meal() + 50),  
  6.         4 : lambda meal: (lambda: meal() + 60)  
  7.         }.get(number, lambda meal: (lambda: meal()))  
  8.   
  9. @sideDish(2)  
  10. @sideDish(3)  
  11. def friedChicken():  
  12.     return 49.0  
  13.   
  14. print('Side Dish3+2=', friedChicken()) # 139.0  
以上的程式都是使用 lambda 建立傳回的函式,若不易理解,以下這個是個較清楚的版本 : 
  1. def sidedish(number):  
  2.     def dish1(meal):  
  3.         return lambda: meal() + 30  
  4.           
  5.     def dish2(meal):  
  6.         return lambda: meal() + 40  
  7.           
  8.     def dish3(meal):  
  9.         return lambda: meal() + 50  
  10.           
  11.     def dish4(meal):  
  12.         return lambda: meal() + 60  
  13.           
  14.     def nodish(meal):  
  15.         return lambda: meal()  
  16.           
  17.     return {  
  18.         1 : dish1,  
  19.         2 : dish2,  
  20.         3 : dish3,  
  21.         4 : dish4  
  22.     }.get(number, nodish)  
  23.   
  24. @sidedish(2)  
  25. @sidedish(3)  
  26. def friedchicken():  
  27.     return 49.0  
  28.      
  29. print(friedchicken()) # 139.0  
Supplement 
[Python 學習筆記] 函式、類別與模組 : 函式 (lambda 運算式)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!