搞定三大神器之 Python 裝飾器

我的施工之路
4列表專題
學(xué)會 Python 裝飾器
裝飾器,幾乎各大Python框架中都能看到它的身影,足以表明它的價值!它有動態(tài)改變函數(shù)或類功能的魔力!
本專題的目錄:
學(xué)會 Python 裝飾器
1 什么是裝飾器
2 裝飾器的結(jié)構(gòu)
3 為什么要這樣
4 裝飾一個函數(shù)
5 裝飾一個類
6 裝飾器層疊
7 溫馨提醒
總結(jié)
1 什么是裝飾器
對于受到封裝的原函數(shù)比如f來說,裝飾器能夠在f函數(shù)執(zhí)行前或者執(zhí)行后分別運(yùn)行一些代碼。
2 裝飾器的結(jié)構(gòu)
裝飾器也是一個函數(shù),它裝飾原函數(shù)f或類cls后,再返回一個函數(shù)g
裝飾一個函數(shù):
def?decorator(f):
??#?定義要返回的函數(shù)
??def?g():
????print('函數(shù)f執(zhí)行前的動作')
????f()
????print('函數(shù)f執(zhí)行后的動作')
??return?g
裝飾一個類:
def?decorator(cls):
??#?定義要返回的函數(shù)
??def?g():
????print('類cls執(zhí)行前的動作')
????f()
????print('類cls執(zhí)行后的動作')
??return?g
使用裝飾器很簡單,@+自定義裝飾器 裝飾要想裝飾的函數(shù)。
3 為什么要這樣
要想理解裝飾器為什么要有這種結(jié)構(gòu),要首先想明白裝飾器的目標(biāo)是什么。
它的價值在于為原函數(shù)f增加一些行為,前提必須不能破壞函數(shù)f,所以肯定不能改變f的內(nèi)部結(jié)構(gòu),所以只能在調(diào)用f前后定義一些行為。
同時,裝飾器函數(shù)decorator返回值又是什么?你可以思考下,返回一個函數(shù)是再好不過的了,它包裝了原函數(shù)f.
4 裝飾一個函數(shù)
printStar函數(shù)接收一個函數(shù)f,返回值也是一個函數(shù),所以滿足裝飾器的結(jié)構(gòu)要求,所以printStar是一個裝飾器。
def?printStar(f):
????def?g():
????????print('*'*20)
????????f()
????????print('*'*20)
????return?g
printStar裝飾器實(shí)現(xiàn)f函數(shù)執(zhí)行前、后各打印20個*字符。
使用printStar:
@printStar
def?f():
????print('hello?world')
調(diào)用:
if?__name__?==?'__main__':
???###?改變函數(shù)功能
???f()
打印結(jié)果:
********************
hello?world
********************
可以很方便的裝飾要想裝飾的其他函數(shù),如下:
@printStar
def?g():
????print('welcome?to?Python')
5 裝飾一個類
除了可以裝飾函數(shù)f外,還可以裝飾類cls,兩者原理都是一樣的。
下面給出一個裝飾器實(shí)現(xiàn)單例模式的例子,所謂單例就是類只有唯一實(shí)例,不能有第二個。
def?singleton(cls):
???instance?=?{}
???def?get_instance(*args,?**kwargs):
???????if?cls?not?in?instance:
???????????instance[cls]?=?cls(*args,?**kwargs)
???????return?instance[cls]
???return?get_instance
定義字典instance,鍵值對分別為類和實(shí)例,這樣確保只cls()一次。
使用裝飾器singleton修飾類:
@singleton
class?CorePoint:
???pass
測試:
if?__name__?==?'__main__':
???###?改變類的功能
???c1?=?CorePoint()
???c2?=?CorePoint()
???print(c1?is?c2)?#?True
6 裝飾器層疊
上面原函數(shù)f不僅能被一個裝飾器修飾,還能被n多個裝飾器修飾。
下面再定義一個裝飾器printLine,被修飾函數(shù)執(zhí)行前后打印20個 -
def?printLine(f):
????def?g():
????????print('-'*20)
????????f()
????????print('-'*20)
????return?g
使用上文定義好的printStar和printLine同時裝飾函數(shù)f:
@printStar
@printLine
def?f():
????print('hello?world')
此時再調(diào)用函數(shù)f:
if?__name__?==?'__main__':
???###?改變函數(shù)功能
???f()
打印結(jié)果:
********************
--------------------
hello?world
--------------------
********************
f被裝飾后,先打印*,再打印 -
層疊多一層,原函數(shù)f就變強(qiáng)大一層。使用裝飾器,還能實(shí)現(xiàn)功能抽離,進(jìn)一步實(shí)現(xiàn)松耦合。
7 溫馨提醒
打印原函數(shù)f的名字__name__,結(jié)果為f
In?[1]:?def?f():?
???...:?????pass?
In?[4]:?f.__name__??????????????????????????????????????????????????????????????
Out[4]:?'f'
但是,被裝飾后函數(shù)名字f變?yōu)間,這不是我們希望的!
@printStar
def?f():
??pass
f()
f.__name__?#?g
Python提供的解決方案:使用functools模塊中的wraps裝飾器:
from?functools?import?wraps
def?printStar(f):
????@wraps(f)
????def?g():
????????print('*'*20)
????????f()
????????print('*'*20)
????return?g
此時再打印被裝飾后f的名字,顯示f,正常!
總結(jié)
學(xué)會 Python 裝飾器
1 什么是裝飾器
2 裝飾器的結(jié)構(gòu)
3 為什么要這樣
4 裝飾一個函數(shù)
5 裝飾一個類
6 裝飾器層疊
7 溫馨提醒
總結(jié)
以上就是裝飾器的核心使用邏輯專題,希望能幫助到各位讀者,若覺得有用,歡迎三連支持。咱們下個專題再見!
推薦各位讀者關(guān)注另一個公眾號《刷題日記》,不僅僅是刷題...
長按兩秒關(guān)注
