測試環境為 CentOS 8 x86_64 (虛擬機)
關於 Syntactic sugar 請參考 – https://zh.wikipedia.org/zh-tw/%E8%AF%AD%E6%B3%95%E7%B3%96
語法糖(英語:Syntactic sugar)是由英國電腦科學家彼得·蘭丁發明的一個術語,指電腦語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程式設計師使用。語法糖讓程式更加簡潔,有更高的可讀性。
在 Python 的 syntax candy(syntactic sugar) 被稱作 Decorator (使用 @ 當做 syntax candy),這邊來看一下 Class 的 Decorator(裝飾器) 與 wrapper 怎麼來使用 ,可以先從 Function 的 Decorator 來了解 – https://benjr.tw/104428
這邊看一下 使用 Class 的方法來寫裝飾詞 (有分有無參數) ,參考範例 – https://www.maxlist.xyz/2019/12/07/python-decorator/
- 範例 : 無參數
[root@localhost ~]# vi classdecorator1.py import time from functools import wraps class Timer(): def __init__(self, func): self.func = func def __call__(self , *args , **kwargs): t_start = time.time() value = self.func(*args, **kwargs) t_end = time.time() t_count = t_end - t_start print('[Time]', t_count) @Timer def dosomethingClass(a, b): print('do some thing') print('a + b = ', a + b) dosomethingClass(1, 2)
執行結果
[root@localhost ~]# python3 classdecorator1.py do some thing a + b = 3 [Time] 3.886222839355469e-05
說明:
匯入時間模組.import time
後面使用到函數 time.time()- 預設回傳從 1970-01-0100:00:00 的秒數(無符號整數)到目前的時間差.
t_start = time.time() t_end = time.time()
在無參數版本的 decorator, 需在 __init__ 時傳入被包裝的函數,我們需自訂函數名稱 self.func 並儲存起來,之後可以在 __call__ 中使用.
def __init__(self, func): self.func = func
在 __call__ 裡面有使用到 self.func 就是指 dosomethingClass 這個函數,這邊還使用到 *args , **kwargs 可以接受任意引數 – https://benjr.tw/104484 .
def __call__(self , *args , **kwargs): value = self.func(*args, **kwargs)
其中的 @ 就是 syntax sugar.
@Timer def dosomethingClass(a, b):
上面步驟等同下面, Timer Class 會把 dosomethingClass 涵式檔做輸入參數,並把回傳的 class 命明為同名的 dosomethingClass .
dosomethingClass = Timer(dosomethingClass(a, b))
- 範例 : 有參數
[root@localhost ~]# vi classdecorator2.py import time from functools import wraps class Timer: def __init__(self, time_sleep): print('[__init__]') print('[time_sleep]:', time_sleep) self.time_sleep = time_sleep def __call__(self, func): @wraps(func) def wrap(*args, **kwargs): t_start = time.time() time.sleep(self.time_sleep) value = func(*args, **kwargs) t_end = time.time() t_count = t_end - t_start print('[Time]', t_count) return value return wrap @Timer(time_sleep=3) def dosomethingClass(a, b): print('do some thing') print('a + b = ', a + b) dosomethingClass(1, 2)
執行結果
[root@localhost ~]# python3 classdecorator2.py [__init__] [time_sleep]: 3 do some thing a + b = 3 [Time] 3.0035414695739746
說明:
有參數時,須在 __init__ 把 decorator 需要的參數傳入並記錄下來,之後在 __call__ 透過 wrapper function 回傳.def __init__(self, time_sleep): print('[__init__]') print('[time_sleep]:', time_sleep) self.time_sleep = time_sleep
從 functools 匯入 wraps , 避免 wrap 之後 __name__ 屬性就會變成 wrap 這個問題.
from functools import wraps
@wraps(func) return wrap
定義了一個名稱為 Timer 的 Class
class Timer:
__init__ 會在 Instance 建立時執行.
def __init__(self, time_sleep): print('[__init__]') print('[time_sleep]:', time_sleep) self.time_sleep = time_sleep
__call__ (請參考 – https://benjr.tw/104454 ) – 當 Instance 作為函數時使用就會去呼叫 __call__ 涵式.裡面透過 wrap (包裝)另一個函數以擴展函數.
def __call__(self, func): @wraps(func) def wrap(*args, **kargs): t_start = time.time() time.sleep(self.time_sleep) value = func(*args, **kargs) t_end = time.time() t_count = t_end - t_start print('[Time]', t_count) return value return wrap
自訂一個函數 dosomething 並 透過 @ 語法糖 (syntax sugar) ,在呼叫 dosomething 去產生 class Timer 的 Instance .
@Timer(time_sleep=3) def dosomethingClass(a, b): print('do some thing') print('a + b = ', a + b)
呼叫 dosomething 函數.
dosomethingClass(1, 2)