Python 裝飾詞 (Decorator) 被大量廣泛的使用在各方 library,是非常實用和必須了解的基礎,本篇介紹了從原理到應用,建議讀者可以跟著敲一次 code,會對裝飾詞有更深入的了解唷~
文章目錄
為什麼需要裝飾詞 decorator?
- 降低程式碼重複率
- 易讀性高
- 靈活度高
Python 裝飾詞的原理
▍首先了解:變數名 與 函式物件 (object)
我們先定義了一個函式為 foo,我們 print(foo),會得到 function foo at 0x1028831e0 表示 foo 是一個變數名,並且指向一個函式物件 (object) 。
如果要調用函式的話,則加上(), foo(),即可調用函式。
def foo():
return ‘bar’
print(foo)
print(foo())
>>> <function foo at 0x1028831e0>
>>> bar
▍開始解說裝飾詞:
首先我們在 def timer(func): 將 func 變數名稱傳入,接下來定義 def wrap(sleep_time) 函式,並且在裡面將剛剛傳入的 func(sleep_time) 調用,這樣就完成一個簡單的裝飾詞囉!
import time
def timer(func):
def wrap(sleep_time):
t_start = time.time()
func(sleep_time)
t_end = time.time()
t_count = t_end - t_start
print('[花費時間]', t_count)
return wrap
def dosomething(sleep_time):
print('do some thing')
time.sleep(sleep_time)
foo = timer(dosomething)
foo(3)
>>> do some thing
>>> [花費時間] 3.004279136657715
1. 語法糖 (Syntax Candy)
如果不想寫成 foo = timer(dosomething),只需要加上 @timer,並直接調用 dosomething() 函式執行
def timer(func):
def wrap(sleep_time):
t_start = time.time()
func(sleep_time)
t_end = time.time()
t_count = t_end - t_start
print(‘[花費時間]’, t_count)
return wrap
@timer
def dosomething(sleep_time):
print(‘do some thing’)
time.sleep(sleep_time)
dosomething(3)
>>> do some thing
>>> [花費時間] 3.003619909286499
2. 裝飾詞的副作用:函式名稱
裝飾詞在被 wrap 包一層後,其 __name__ 屬性就會被修改成 wrap
def timer(func):
def wrap():
t_start = time.time()
func()
t_end = time.time()
t_count = t_end - t_start
print(‘[花費時間]’, t_count)
return wrap
@timer
def dosomething():
print('do some thing')
dosomething()
print(dosomething.__name__)
>>> wrap
如果要消除這個副作用的話,可以使用 python 內建的 functools,只需要在 def wrap()之前,加上 @wraps(func),即可獲得原先的 __name__ 屬性 dosomething。
from functools import wraps
def timer(func):
@wraps(func)
def wrap():
t_start = time.time()
func()
t_end = time.time()
t_count = t_end - t_start
print(‘[花費時間]’, t_count)
return wrap
@timer
def dosomething():
print('do some thing')
dosomething()
print(dosomething.__name__)
>>> dosomething
3. 裝飾詞的觸發先後順序
如果有兩個 Decorator 裝飾詞要使用的話怎麼辦,其實只需要加在上面一行即可,順序的話會從上而下觸發
def timer(func):
@wraps(func)
def wrap():
t_start = time.time()
func()
t_end = time.time()
t_count = t_end - t_start
print(‘[花費時間]’, t_count)
return wrap
def func_print_one(func):
@wraps(func)
def wrap():
print(‘this is func_print_one’)
func()
return wrap
def func_print_two(func):
@wraps(func)
def wrap():
print('this is func_print_two')
func()
return wrap
@timer
@func_print_one
@func_print_two
def dosomething():
print('do some thing')
dosomething()
>>> this is func_print_one
>>> this is func_print_two
>>> do some thing
>>> [花費時間] 2.3126602172851562e-05
4. 裝飾詞的參數 (*args, **kargs)
只需在 def wrap() 和 func() 中加入 *args, **kargs 即可調用參數,範例如下:
def timer(func):
@wraps(func)
def wrap(*args, **kargs):
t_start = time.time()
value = func(*args, **kargs)
t_end = time.time()
t_count = t_end - t_start
print('[花費時間]', t_count)
return value
return wrap
def func_print(func):
@wraps(func)
def wrap(*args, **kargs):
print('this is func_print')
value = func(*args, **kargs)
return value
return wrap
@timer
@func_print
def dosomething(a, b):
print('do some thing')
print(a + b)
dosomething(1, 2)
>>> this is func_print
>>> do some thing
>>> 3
>>> [花費時間] 1.52587890625e-05
5. 用 Class 寫裝飾詞
如果是 Class 的方法來寫裝飾詞的話,會將 wrap 寫在 __call__ 裡面來調用,範例如下:
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, **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('[共花費時間]', 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)
>>> [__init__]
>>> [time_sleep]: 3
>>> do some thing
>>> a + b = 3
>>> [共花費時間] 3.0002880096435547
本篇簡單的介紹了裝飾詞的原理、語法糖的使用、函式名稱的副作用、觸發的先後順序、帶參數和如何使用 Class來寫裝飾詞,建議可以實際將 code 打一次,會對裝飾詞有更深的了解唷~
關於 Python 物件導向教學的延伸閱讀:
▍本站的其他相關教學:
- [Python教學] 一切皆為物件,到底什麼是物件 Object ?
- [Python教學] 物件導向 – Class 類的 封裝 / 繼承 / 多型
- [Python教學] Class / Static / Abstract Method 初探
- [Python教學] @property 是什麼? 使用場景和用法介紹
- [Python教學] 裝飾詞原理到應用
- [Python教學] dataclass 是什麼? (python 3.7+)
那麼 [Python教學] 裝飾詞原理到應用 結束囉,感謝收看!如有任何問題,歡迎底下留言或私訊,我會盡快回覆您