Python 用裝飾房子來分享 Decorator 裝飾器
分類
說明
起初我是從設計模式(Design Pattern)看到其中一個叫做裝飾者模式(Decorator Pattern),Python 剛好有內建的語法支援 Decorator (後面以裝飾器稱呼),自己偶爾會看到別人寫的程式會用到它,但又常常忘記怎麼用,導致有點看不懂別人在寫什麼,所以我打算寫一篇用 Python 裝飾房子的程式。
最簡單的裝飾器
要裝飾房子之前要先取得一棟房子
def get_house():
print('我取得了一棟房子')
get_house()
輸出:
我取得了一棟房子
這棟房子裡面有人信耶穌,所以要放一個十字架
def get_house():
print('我取得了一棟房子')
def put_cross(original_function):
def wrap():
original_function()
print('我放了一個十字架')
return wrap
decorator = put_cross(get_house)
decorator()
輸出:
我取得了一棟房子
我放了一個十字架
這裡需有一個裝飾器的概念,就是目的要在不影響原本的功能進行擴充,所以將原本 get_house()
包裹在 put_cross()
裡面,就好像房子是禮物,十字架是包裝紙。
如果原本的函式有參數,可以改用下面的範例,取得指定顏色的房子。
def get_house(color):
print(f'我取得了一棟{color}色的房子')
def put_cross(original_function):
def wrap(color):
original_function(color)
print('我放了一個十字架')
return wrap
decorator = put_cross(get_house)
decorator('黃')
輸出:
我取得了一棟黃色的房子
我放了一個十字架
語法糖(Syntactic sugar)
語法糖是一種概念,它可以使程式碼更易讀、更容易理解和更簡潔,並不是專指 Python 裝飾器,很多程式都有用到語法糖的概念,下列為裝飾器的語法糖。
def put_cross(original_function):
def wrap(color):
original_function(color)
print('我放了一個十字架')
return wrap
@put_cross
def get_house(color):
print(f'我取得了一棟{color}色的房子')
get_house('黃')
輸出:
我取得了一棟黃色的房子
我放了一個十字架
@put_cross
簡化了呼叫裝飾器函式,當你知道原本的程式後,你就會發覺這樣寫更容易理解。
多個裝飾器,其先後順序
這房子裡面還有一個是拜媽祖的,所以要再放一尊媽祖。
def put_mazu(original_function):
def wrap(color):
original_function(color)
print('我放了一尊媽祖')
return wrap
def put_cross(original_function):
def wrap(color):
original_function(color)
print('我放了一個十字架')
return wrap
@put_mazu
@put_cross
def get_house(color):
print(f'我取得了一棟{color}色的房子')
get_house('黃')
輸出:
我取得了一棟黃色的房子
我放了一個十字架
我放了一尊媽祖
從上面的輸出,可以看到原本函式會是這樣呼叫的
decorator = put_mazu(put_cross(get_house))
decorator('黃')
所以裝飾器會從外往內包裹。
裝飾器參數
如果裝飾器要帶參數,只需要在裝飾器外面包裹一個函式作為參數即可。
def put_mazu(original_function):
def wrap(color):
original_function(color)
print('我放了一尊媽祖')
return wrap
def put_cross(cross_color):
def wrap_put_cross(original_function):
def wrap(color):
original_function(color)
print(f'我放了一個{cross_color}色的十字架')
return wrap
return wrap_put_cross
@put_mazu
@put_cross('白')
def get_house(color):
print(f'我取得了一棟{color}色的房子')
get_house('黃')
輸出:
我取得了一棟黃色的房子
我放了一個白色的十字架
我放了一尊媽祖
原本的 put_cross
變成 wrap_put_cross
,新的 put_cross
新增了一個 cross_color
的參數,然後裝飾器就可以呼叫參數 @put_cross('白')
。
用類別作為裝飾器
裝飾器類別有帶參數跟沒帶參數的用法不一樣,這個水很深,我沒有深入了解,以下僅提供這兩種範例。
無參數類別裝飾器
class God:
def __init__(self, original_function):
self._original_function = original_function
def __call__(self, color):
self._original_function(color)
print('我放了一尊神明在這')
@God
def get_house(color):
print(f'我取得了一棟{color}色的房子')
get_house('黃')
輸出:
我取得了一棟黃色的房子
我放了一尊神明在這
有參數類別裝飾器
class God:
def __init__(self, name):
self._name = name
def __call__(self, original_function):
def decorate(*args, **kwargs):
original_function(*args, **kwargs)
print(f'我放了一尊{self._name}在這')
return decorate
@God('媽祖')
def get_house(color):
print(f'我取得了一棟{color}色的房子')
get_house('黃')
輸出:
我取得了一棟黃色的房子
我放了一尊媽祖在這
結論
本篇只說明了基礎的裝飾器用法,我在網路上還有看到許多裝飾器進階用法,例如使用裝飾器會改變函式名稱,因為實際上呼叫的是已經裝飾的函式,這時就可以利用 from functools import wraps
,在裝飾器裡面使用 @wraps 裝飾器,反裝飾回去進而取得原函式名稱,應該是這樣,當然還有很多不錯的用法,大家可以自行研究。
參考
一杯咖啡的力量,勝過千言萬語的感謝。
支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!