Python學習筆記之裝飾器
裝飾器簡單來說,就是現有的物件,在不修改原始碼和呼叫方式的情況,對現有的物件新增新的功能,比如插入日誌,許可權校驗之類的。
為什麼要用裝飾器?
1、現在有這麼一個物件,我要給這個物件新增一個列印日誌的功能
1 def foo(): 2 print("I'm foo function")
2、那麼可以這樣改
1 def foo(): 2 print("I'm foo function") 3 print("logging")
但是如果有10個物件,是不是要在10處加上列印日誌的程式碼?那如果是50個,100個呢?這樣會有大量重複的程式碼出現
3、那還可以這樣改,把foo當作一個引數傳給日誌功能不就好了麼
1 def foo(): 2 print("I'm foo function") 3 4 def logging(func): 5 print("logging") 6 func() 7 8 logging(foo)
這樣會有一個情況就是,修改了呼叫方式,假如這段程式碼已經在生產環境中運行了,而且有很多地方都引用這段程式碼,不可能把所有已經引用的地方全都修改了吧
這時候就需要用到裝飾器了,不用修改原來物件的程式碼和呼叫方式,新增新的功能,還可以避免寫重複的程式碼
函式可以賦值給變數
例如
1 def foo(): 2 print("I'm foo function") 3 4 f = foo 5 f() 6 7 #輸出結果 8 I'm foo function
函式名後面有小括號和沒有小括號的區別
1 def foo(): 2 print("I'm foo function") 3 4 print(foo) #沒有小括號表示函式的記憶體地址 5 print('---我---是---分---割---線---') 6 print(foo()) #有小括號表示函式的執行結果 7 8 #輸出結果 9 <function foo at 0x0000026EE94C8F70> 10 ---我---是---分---割---線--- 11I'm foo function 12 None
為什麼要說這個函式可以賦值和有沒有小括號的問題呢,因為下面會用到這個
沒有引數的物件進行裝飾
1、還是這個例子,為其新增日誌功能
1 def foo(): 2 print("I'm foo function")
2、下面看一下兩個例子
(1)語法糖的寫法
1 def logging(func): #1 2 def deco(): #3 3 print("logging") #6 4 func() #7 5 return deco #4 6 7 @logging #2 8 def foo(): 9 print("I'm foo function") #8 10 11 foo() #5 12 13 #輸出結果 14 logging 15 I'm foo function
(注:#1 #2 #3 ....#8 其表示的意思是在除錯模式下的執行順序)
(2)重新賦值的寫法
1 def logging(func): #1 2 def deco(): #4 3 print("logging") #7 4 func() #8 5 return deco #5 6 7 def foo(): #2 8 print("I'm foo function") #9 9 10 foo=logging(foo) #3 11 foo() #6 12 13 #輸出結果 14 logging 15 I'm foo function
(注:#1 #2 #3 ....#9 其表示的意思是在除錯模式下的執行順序)
這兩個例子是等價關係,語法糖@logging的寫法,相當於隱式的做了foo=logging(foo)
logging(foo)返回的是deco這個函式的記憶體地址,被重新賦值給了foo這個函式名,所以現在foo=deco
當執行foo()的時候,其實就是deco()
需要注意的是foo和foo()這兩個的區別,foo是表示一個記憶體地址,而foo()表示的是執行結果
有引數的物件進行裝飾
1 def logging(func): 2 def deco(*args, **kwargs): # *args表示可以接收任意個位置引數,**kwargs表示可以接收任意個關鍵字引數, 位置引數*args要放在關鍵字引數**kwargs的前面 3 print("logging") 4 func(*args, **kwargs) 5 return deco 6 7 @logging 8 def foo(): 9 print("My name is foo function") 10 11 @logging 12 def bar(a, b): 13 print("My name is %s %s"%(a, b)) 14 15 @logging 16 def ten(x, y): 17 print("My name is %s %s"%(x, y)) 18 19 20 foo('foo') 21 print('---我---是---分---割---線---') 22 bar('bar', 'function') 23 print('---我---是---分---割---線---') 24 ten('ten', y='function') 25 26 #輸出結果 27 logging 28 My name is foo function 29 ---我---是---分---割---線--- 30 logging 31 My name is bar function 32 ---我---是---分---割---線--- 33 logging 34 My name is ten function
可適用於不帶引數的物件、帶引數的物件,以及關鍵字引數的物件的裝飾
當然還有一些更高階的用法,比如裝飾器也帶引數的
可參考以下兩個文章:
https://www.cnblogs.com/arvin-feng/p/11108799.html
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
若以上內容表述有誤,歡迎各位大神指導一下。