Python22章 裝飾器

由於有時候一天看的內容比較多,寫在一章比較亂而且後期不好找,寫多章一天發了好幾天不太真實,所以天改爲章
2019/11/6 沒有暖氣凍死了快

軟件設計原則之一 —> 開閉原則

對功能的擴展開放:可以隨意增加或者減少功能
但是,可以加功能,但是不能也不需要修改源代碼,這樣子纔是最穩定的
對代碼修改是封閉的

裝飾器

先來個小故事,女媧造人

def zaoren():
    print("捏個泥人")
    print("吹口仙氣")
    print("你就出來了")

zaoren()
zaoren()
zaoren()

每造一個人,就調用一次函數,就可以了
但是,之前可以這麼做,現在來了三年大旱,沒有水了,也就沒有了泥,現在造人之前要先和泥.
但是現在大旱需要水和泥,但是過幾天可能就下雨,不需要了和泥.
這個時候就可以選擇用裝飾器,因爲有個功能有時需要又是不需要.

def water():
    print("和泥")
    zaoren()

這個時候可以寫成這樣子,等到不需要這個功能,還是可以直接調用zaoren()函數.
但是,之前的zaoren函數都需要改成water,工程量很大.所以此時需要裝飾器.

裝飾器雛形

def zhuangshi(fn):  #傳遞進一個函數
   def haha():
       print("和泥")
       fn()  #調用這個函數
   return haha
def zaoren():
   print("捏個泥人")
   print("吹口仙氣")
   print("你就出來了")

#qwe = zhuangshi(zaoren) #前面接受的就是一個變量,但是這樣子調用還是和之前zaoren不同了,所以改成zoaren
zaoren = zhuangshi(zaoren)

zaoren() #輸出:和泥 \n捏個泥人 \n 吹口仙氣 \n 你就出來了

#qwe = zhuangshi(zaoren) #前面接受的就是一個變量,但是這樣子調用還是和之前zaoren不同了,所以改成zoaren
zaoren = zhuangshi(zaoren)

zaoren() #輸出:和泥 \n捏個泥人 \n 吹口仙氣 \n 你就出來了

#但是,不只有造人需要和泥,蓋房子也需要.那麼
def jian():
   print("蓋房子")
jian = zhuangshi(jian)

jian()  #輸出:和泥 \n 蓋房子
#此時,所有和和泥相關操作都可以調用,並且可以再之前或者之後添加功能.
def game():
    print("打開GAT")
    print("搶一架飛機")
    print("跟警察死磕")
#現在遊戲前想開掛
def gua(play):
    def wai():
        print("打開外掛")
        play()
        print("關閉外掛")
    return wai
game = gua(game)
game()  #輸出:打開外掛 \n 打開GAT \n 搶一架飛機 \n 跟警察死磕 \n 關閉外掛


# 此時,還可以適用別的
def cf():
    print("打開CF")
    print("殺人")
    print("勝利")

cf = gua(cf)
cf()  #輸出: 打開外掛 \n 打開CF \n 殺人 \n 勝利 \n 關閉外掛


def game(username,password):
    print("打開CF")
    print("登陸",username,password)
    print("殺人")
    print("勝利")
#現在調用需要傳遞參數
def gua(en):
    def haha(username,password):
        print("開掛")
        en(username,password)
    return haha
game = gua(game)
game("liulaoliu",123)  # 輸出:開掛  打開CF  登陸 liulaoliu 123  殺人  勝利
#但是現在想玩其他的GTA,GTA不需要QQ登陸,只需要一個用戶名

def game(username):
    print("打開GAT")
    print("用戶名",username)
    print("搶一架飛機")
    print("跟警察死磕")
#裝飾器改爲:
def gua(en):
    def haha(*username,**password):
        print("開掛")
        en(*username,**password)
    return haha
#這樣子就可以只傳遞一個參數
game = gua(game)
game("wulaowu")
# 輸出:開掛  打開GAT  用戶名 wulaowu  搶一架飛機  跟警察死磕

def game(username,password):
    print("打開GAT")
    print("用戶名",username,password)
    print("搶一架飛機")
    print("跟警察死磕")
    return ("十萬金幣")
qwe = game(111,222)
print(qwe) # 此處調用函數會到一個return的返回值 "十萬金幣"

def gua(en):
    def haha(*username,**password):
        print("開掛")
        en(*username,**password)  #此處改寫爲以下兩行,接收函數的返回值,就不會輸出None了.
        #qwe = en(*username,**password)  #函數在此處進行裝飾調用的時候沒有接收返回值,所以輸出None,改寫爲這樣子接收返回值
        #return qwe  #並且在此處進行了返回,函數正常調用
    return haha
game = gua(game)
wer = game("123",456)
print(wer) #但是現在經過裝飾器之後沒有了返回值 ,只有一個None


通用裝飾器語法!

# 存在意義: 在不破壞原來函數的前提下,給原來代碼插入一些新的功能
#Python中的動態代理
def func(fn):
    def inner(*args,**kwargs):
        '''執行目標函數之前要進行的操作'''
        ret = fn(*args,**kwargs)
        '''在進行目標函數之後要進行的操作'''
        return ret
    return inner
@func #這裏就相當於:target_func = func(target_func)
def target_func():
    pass

target_func()  #此時執行的是inner

帶有參數的裝飾器

def wrapper(fn):
    def inner(*args,**kwargs):
        print("問問金老闆行情,怎麼樣啊")
        ret = fn(*args,**kwargs)
        print("金老闆騙我,恨你")
        return ret
    return inner
@wrapper
def yue():
    print("走啊,約嗎")
yue()
# 但是現在有的時候需要問,有的時候不需要問.此時,寫爲
def wrapper_out(flag):
    def wrapper(fn):
        def inner(*args,**kwargs):
            if flag == True:
                print("問問金老闆行情,怎麼樣啊")
                ret = fn(*args,**kwargs)
                print("金老闆騙我,恨你")
                return ret
            else:
                ret = fn(*args, **kwargs)
                return ret
        return inner
    return wrapper
@wrapper_out(True)  #先執行wrapper_out,返回wrapper 可以填寫True或者False來控制函數調用
#wrapper_out只是判斷,實際調用韓式wrapper
def yue():
    print("走啊,約嗎")
yue()

多個裝飾器裝飾同一個函數,會就近原則,先從函數最近的函數開始依次裝飾,不論先後.

發佈了130 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章