關於裝飾器,在面試時,經常會被問到這兩個問題:
1、你都用過裝飾器實現過什麼樣的功能?
2、請手寫乙個可以傳參的裝飾器?
這篇部落格就根據這兩個問題,帶大家系統的學習裝飾器的所有內容.希望對大家有所幫助.
hello,裝飾器
入門: 日誌列印器
入門: 時間計時器
高階: 帶引數的函式裝飾器
高階: 不帶引數的類裝飾器
高階: 帶引數的類裝飾器
使用偏函式與類實現裝飾器
裝飾類的裝飾器
wraps裝飾器的作用
內建裝飾器: property
其他裝飾器: 裝飾器實戰
裝飾器的使用方法很簡單:
1. 先定義乙個裝飾器
2. 再定義你的業務函式或者類
3. 最後把裝飾器加在這個函式上面
舉個小栗子:
defdecorator(func):
return
func()
return
@decorator
deffunction():
print("
hello, decorator
")
實際上,裝飾器並不是編碼必須性,意思就是說,你不使用裝飾器完全可以,它的出現,應該是使我們的**
實現的功能:
1. 在函式執行前,先列印一行日誌,通知要執行函式了
2. 函式執行完後,再列印一行日誌,宣布函式執行完畢.
deflog(func): #裝飾函式,引數 func 是被裝飾的函式
def inner(*args,**kwargs):
print('
start running: {} function
'.format(func.__name__
)) res = func(*args,**kwargs) # 執行主函式
print('
stop running')
return
res
return
inner
@log
defmain():
print('
我是主函式
')
輸出結果:
start running: main function我是主函式
stop running
時間計時器,顧名思義,就是實現乙個能夠計算函式執行時長的功能.
importtime
deftimer(func):
def inner(*args,**kwargs):
start =time.time()
func(*args,**kwargs) #函式真正執行的地方
print('
執行了 {}s
'.format(time.time()-start)) #計算時長
return
inner
@timer
defmain(sleep_time):
time.sleep(sleep_time)
main(10)
輸出結果:
執行了 10.0073800086975098s
通過上面兩個簡單的入門例項,大家應該能體會到裝飾器的工作原理了.
不過,裝飾器的用法還遠不止如此. 回過頭去看看上面的例子,裝飾器是不能接收引數的。其用法,只能適用於一些簡單的場景。不傳參的裝飾器,只能對被裝飾函式,執行固定邏輯。
裝飾器本身是乙個函式,做為乙個函式,如果不能傳參,那這個函式的功能就會很受限,只能執行固定的邏輯。這意味著,如果裝飾器的邏輯**的執行需要根據不同場景進行調整,若不能傳參的話,我們就要寫兩個裝飾器,這顯然是不合理的。
比如說,我們要迴圈執行某乙個函式,迴圈的次數是隨機指定的.
defloop(count):
defwrap(func):
def inner(*args,**kwargs):
for i in
range(count):
func(i)
return
inner
return
wrap
@loop(6)
defmain(i):
print('
第 {} 次迴圈
'.format(i+1))
main()
輸出結果:
第 1次迴圈第 2次迴圈
第 3次迴圈
第 4次迴圈
第 5次迴圈
第 6 次迴圈
以上都是基於函式實現的裝飾器,在閱讀別人**時,還可以時常發現還有基於類實現的裝飾器。
基於類裝飾器的實現,必須實現__call__
和__init__
兩個內建函式。
__init__
:接收被裝飾函式
__call__
:實現裝飾邏輯。
還是以日誌列印這個例子為例.
classlog:
def__init__
(self,func): #接收被裝飾函式
self.func =func
def__call__(self, *args, **kwargs): #實現裝飾邏輯
print('[info]:
{} 正在執行
'.format(self.func.__name__
)) res =self.func(*args,**kwargs)
print('
函式執行完畢')
return
res@log
defmain():
print('
我是主函式')
main()
執行結果:
[info]: main 正在執行我是主函式
函式執行完畢
上面不帶引數的例子,只能列印 info 級別的日誌,正常情況下,我們還需要列印 debug, warning 等級別的日誌.這就需要給類裝飾器傳入引數,指定日誌級別了.
帶引數和不帶引數的類裝飾器有很大不同:
__init__
:不再接收被裝飾函式,而是接收傳入引數。
__call__
:接收被裝飾函式,實現裝飾邏輯。
classlogger():
def__init__(self,level='
info
'): #接收引數
self.level =level
def__call__
(self, func): # 接收被裝飾函式,實現裝飾邏輯
def wrap(*args,**kwargs):
print('
[{}]正在執行 {}
'.format(self.level,func.__name__
))
return func(*args,**kwargs)
return
wrap
@logger(level='
warning')
defmain():
print('
我是主函式')
main()
執行結果:
[warning]正在執行 main我是主函式
*參考:
Phoenix入門到精通
摘要 此phoenix系列文章將會從phoenix的語法和功能特性 相關工具 實踐經驗以及應用案例多方面從淺入深的闡述。希望對phoenix入門 在做架構設計和技術選型的同學能有一些幫助。phoenix是乙個開源的hbase sql層。它不僅可以使用標準的jdbc api替代hbase client...
GIT入門到精通
git工作流 版本回退 分支管理 標籤管理 遠端倉庫 linux torvalds在 1991 年建立了開源的 linux,從此,linux系統不斷發展,已經成為最大的伺服器系統軟體了。linux雖然建立了linux,但linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地為linux編寫...
Git 入門到精通
git是乙個開源的分布式版本控制系統,用以有效 高速的處理從很小到非常大的專案版本管理。git的特點 git 是用於 linux核心開發的版本控制工具。與常用的版本控制工具 cvs,subversion 等不同,它採用了分布式版本庫的方式,不必伺服器端軟體支援 wingeddevil注 這得分是用什...