1. 程式人生 > >python裝飾器測試與理解

python裝飾器測試與理解

第一,裝飾器基礎語句

# 這是一個基本的裝飾器測試檔案
def deco(func):
    def mod():
        func()
        print("I'm mod")
        #mod函式在接收的func函式基礎上,後置增加了print功能
    return mod
    # 將func拓展為mod後,返回新函式mod。
    # 這,就是本deco函式的使命。

# 所以假設main函式如下:
def main():
    print("I'm main")
    pass
# 則可以有:
a = deco(main) # 滿足deco的引數要求,使其完成使命
a() # 輸出結果為: #I'm main #I'm mod # 鑑於deco(main)返回的本質是一個函式,甚至可以有: deco(main)() # 輸出結果同樣為: #I'm main #I'm mod

第二,它是不是裝飾器?裝飾器的執行邏輯是什麼?

# 裝飾器的執行邏輯
def deco(func):
    print("I'm deco")
    def mod():
        print("I'm mod")
        pass
    return func
    #這一次將func原樣返回,mod也未對func作修改。

def main():
    print
("I'm main") a = deco(main) a() # 執行結果為: # I'm deco # I'm main # I'm main # 為什麼deco在前? # 因為a = deco(main)本身也產生輸出,而a()產生的輸出只有一句 # 不信試試這個: a() deco(main) # 輸出結果為: # I'm main 由a()生成 # I'm deco 由deco函式生成 #總結: # 1.裝飾器一般分內外2層,才能達到‘裝飾’的目的。 # 2.內層必須對其對func函式有所操作,否則無法起到 # ‘裝飾器的作用’。 # 3.a()的本質,是執行且僅執行一個函式,
# 就是deco返回的那個函式。

第三,@語法糖

標準的@語法糖

# @語法糖
def deco(func):
    def mod():
        func()
        print("I'm mod")

    return mod

@deco
def main():
    print("I'm main")

main()
# 最終輸出為:
# I'm main
# I'm mod

# @deco這一行語句,實現瞭如下的基本功能:
#  1.實現了deco(main)這條語句
#  2.實現了將deco(main)賦值給一個也叫main的變數。
#    這相當於a=a+5。 a+5的值,完成了對原來a的覆蓋
# 綜上,@deco這一句,相當於,且僅相當於main = deco(main)

# 這樣寫的好處:
#  1.沒有改變main的呼叫方法。(儘管內部新main替換了老main)
#  2.沒有改變原main函式的內部性質。(依然可以打印出I'm main)
# 以上也是一個良好裝飾器的必備特質。例如本例中def的deco
#  ,就具備這樣的特質

@語法糖的執行邏輯

# @語法糖的執行邏輯
def mod2(func):
    func()
    print("I'm mod2")
    return mod2

@mod2
def main2():
    print("I'm main2")

# main2()
# 以上是正常程式,並作了2處改動:
# 1.這一次我沒有呼叫main2(),將它註釋掉了。
# 2.原本deco函式內還定義了一層mod,這次將deco外殼刪掉
#   只留下了mod

#最終輸出:
# I'm main2
# I'm mod2

# 那麼@mod2,相當於執行了一次mod2內的所有語句。注意,
# 這是在沒有進行main2()語句呼叫時,就執行過一次的。
# 參考‘它是不是裝飾器?’的內容,相當於執行了mod2(main2)這
# 一語句。
# 下面,你可以試試將mian2()的註釋去掉,看看會發生什麼。

帶返回值的@語法糖

# 帶引數的語法糖
def deco(func):
    def mod():
        temp = func()
        print("I'm mod")
        return temp

    return mod

@deco
def main():
    print("I'm main")
    return "I'm main return"

a = main()
print('----分割線')
print(a)
# 最終輸出:
# I'm main
# I'm mod
# ----分割線
# I'm main return

# 裝飾器是圍繞原函式服務的。本例中,因為main多了return語句,所以
# 裝飾器應當為保留return 內的資訊,而作出改變。在本例中,多了
# return temp及其配合語句temp = func(),來實現引數傳遞。傳遞線路為:
# 老main函式的'I'm main return' ,通過func(),傳遞給temp, 
# 由temp返回給mod函式,mod函式加上了其它拓展後,新函式mod又
# 寫覆蓋給新main函式,使return在新main函式中實現。

帶引數的@語法糖

# 帶引數的語法糖
def deco(func):
    def mod(arg1, arg2):
        temp = func(arg1, arg2)
        print("I'm mod")
        return temp

    return mod

@deco
def main(a, b):
    print(a + b)
    return "I'm main return"

a = main(1, 1)
print(a)
# 最終輸出:
# 2
# I'm mod
# I'm main return   #這一句由print(a)輸出

# 比較簡單,還是圍繞main作裝飾器修改。當def mian時,
# 出現a,b兩個引數時,deco中的mod函式,也應當對應出現
# 兩個引數,這裡取名為arg1, arg2。最終main(1,1)
# 呼叫時,既完成了1+1的操作,也完成了輸出"I'm mod"的操作。

帶可變引數的@語法糖

# 帶引數的語法糖
def deco(func):
    def mod(*x1, **x2):
        temp = func(*x1, **x2)
        print("I'm mod")
        return temp

    return mod

@deco
def main(a, b, c, d):
    print(a + b + c + d)
    return "I'm main return"

a = main(1, 1, 1, 1)
print(a)
# 最終輸出:
# 4
# I'm mod
# ----分割線
# I'm main return   #這一句由print(a)輸出

# 依舊,當main變為4個引數時,我們對裝飾器作被動修改。當def mian時,
# 但程式設計師想偷懶了,索性在def mod時,修改引數為*x1和**x2,大功告成!

參考