python__高級 : @修飾器(裝飾器)的理解


以下是第一次了解的時候寫的東西,有的地方理解不正確,雖已改正但是太片面,請直接看下面第二次修改加上的內容.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

裝飾器本質上是一個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---

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM