以下是第一次了解的時候寫的東西,有的地方理解不正確,雖已改正但是太片面,請直接看下面第二次修改加上的內容.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能.
裝飾器的作用就是為已經存在的對象添加額外的功能。
def funA(fun): print (fun()) def funB(): print ('B') return 1 funA(funB) ----------------------------- >>> B 1
可以看出,這個程序的運行過程為:
1.執行函數funA,把funB當作參數傳進去, print(fun()) 這一句 執行了 funB, 然后打印 'B' , 返回 1
2. print(fun()) 這一句把 返回的 1 打印出來
而修飾器的作用:
def funA(fun): #函數本身也是對象,所以可以將函數作為參數傳入另一函數並進行調用,而funB是有返回值的,所以結果輸出了返回值1.(個人理解) print (fun()) @funA def funB(): print ('B') return 1 ---------------------- >>> B 1
作用相當於 funB = funA(funB),不過只能放在一個函數或者類定義之前
需要注意的是,如果funB在funA里沒用被調用的話,那funB是不會被執行的,如:
def funA(fun): print ('funA') @funA def funB(): print ('B') return 1 --------------------------- >>> funA
可以看出,只執行了funA而funB沒有被執行,因為print('B')並沒有被打印出來.
ps:如果funA不加參數的話,比如直接 def funA(): 這樣定義,他是會報錯的:
@funA TypeError: funA() takes 0 positional arguments but 1 was given
大意是@funA中的funA必須要給他提供一個參數,但是你給了0個.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第二次修改:
第一次了解的是一些淺層次的東西,把它深入一下,看個例子:
def w1(func): print('正在裝飾') def cou(): print('22222222222') func() return cou @w1 def f1(): print('11111111111') >>>正在裝飾
可以看出,裝飾器 @w1 這一行,其實在函數沒有被調用之前已經執行了, 這一句就等於 f1=w1(f1) 所以 w1 函數已經被調用了,返回的是 cou函數的引用,
所以說如果再調用 f1() ,其實執行的是 cou() ,而真正的 f1 函數的引用現在正被保存在 w1 函數中的 func參數里面,
(這兒可以當作閉包的一個表現,即當函數中有東西外邊還有引用指向它的時候,它並不會立即回收,而是保存了這個函數的空間)
兩層裝飾: 例子:
def w1(func): print("---正在裝飾1----") def inner(): print("---1111111111----") func() return inner def w2(func): print("---正在裝飾2----") def inner(): print("---2222222222----") func() return inner @w1 @w2 def f1(): print("---f1---") >>>---正在裝飾2---- ---正在裝飾1----
從運行結果可以看出,首先調用裝飾器w2,再調用裝飾器w1,也就是說 運行到 @w1 這一行,因為在它下面的並不是一個函數,所以w1先暫停,先調用w2,w2裝飾完成之后,返回的是w2 的 inner 函數的引用,
w1 再開始對 w2 的inner 函數進行裝飾. 最后返回的是w1 的 inner 函數.如果最后調用 f1() 那么運行結果為:
---正在裝飾2---- ---正在裝飾1---- ---1111111111---- ---2222222222---- ---f1---
因為 這個時候調用 f1() 其實 調用的是 w1的 inner 函數,所以首先打印 --11111--- ,然后 執行 func() 這個func() 也就是 w2 的inner, 所以再打印 ---222222----, 下一句 fun() 才是真正的 f1() 函數,打印 ---f1---
裝飾有參數的函數: 被裝飾的函數有參數的話,可以這樣:
def w1(func): print("---正在裝飾1----") def inner(*args, **kwargs): print("---1111111111----") func(*args, **kwargs) return inner @w1 def f1(a): print("---%d---" % a) f1(123) >>>---正在裝飾1---- ---1111111111---- ---123---
在 inner 函數里面加上 接受無名參數和關鍵字參數,然后 func(*args, **kwargs) 把接收到的參數原封不動的傳回 f1 函數里面去,這樣 f1 無論有多少個參數,都可以給他傳回去.
那么,如果被裝飾的函數有返回值,同樣,在 inner里面把函數返回的東西用個變量保存起來,然后 在inner 里面return 即可:
def w1(func): print("---正在裝飾1----") def inner(*args, **kwargs): print("---1111111111----") result = func(*args, **kwargs) # <---------------------- return result # <---------------------- return inner @w1 def f1(a): print("---%d---" % a) return 456 a = f1(123) print(a) >>>---正在裝飾1---- ---1111111111---- ---123--- 456
可以看出 a 成功保存了返回的結果 456 .
如果,對裝飾器進行調用,如 @w1() 后面帶個括號, 結果會怎樣:
def w1(): print("---正在裝飾1----") def inner(func): print("---1111111111----") return inner @w1() def f1(): print("---f1---") >>>---正在裝飾1---- ---1111111111----
可以看出,雖然沒有調用f1,但是竟然連里面的inner函數也被執行了一遍,因為輸出了 ---111111111111-----,這說明,如果 @w1() 這樣用 ,那么它首先會 把 w1() 函數執行一遍 , 這個時候返回的是 inner 函數的引用,
那么,@w1() 就變成了 @inner 這個時候 再把f1傳到了inner函數里面開始進行裝飾 所以 inner 函數被執行,
利用這個特點,可以在 裝飾器中帶有參數 ,只不過為了防止調用,需要在外面再加上一層:
def a1(nihao): def w1(func): print("---正在裝飾1----") def inner(): print("---1111111111----%s" % nihao) func() return inner return w1 @a1('hello~') def f1(): print("---f1---") >>>---正在裝飾1----
過程 1. 首先執行 a1('hello~') a1里面用 nihao 這個變量保存傳遞的參數,返回的是 w1 的引用
2. 裝飾器那一行 變成了 @w1 ,然后把 f1 傳遞進去,調用 w1 開始進行裝飾
3. 裝飾完成后 返回的 是 inner 的引用 所以 現在 f1 = inner
如果調用 f1() 則正常執行,還可以在 inner 中把傳遞進去的參數打印出來:
>>>
---正在裝飾1----
---1111111111----hello~
---f1---