python裝飾器

開閉原則:
在不修改原函數及其調用方式的狀況下對原函數功能進行擴展
對代碼的修改是封閉
不能修改被裝飾的函數的源代碼
不能修改被裝飾的函數的調用方式python

用函數的方式設想一下游戲裏用槍的場景web

複製代碼
 1 def game():  2 print('壓子彈')  3 print('槍上膛')  4 print('發射子彈')  5 game()  6 game()  7 game()  8  9 此時須要給槍增長一個瞄準鏡,好比狙擊遠程目標時候須要加,狙擊近程目標不用加 10 此時上邊的代碼就變成了如今的代碼 11 12 def sight(): 13 print('專業狙擊瞄準鏡') 14  game() 15 sight() 16 sight() 17 sight() 18 此時的設計就不符合開閉原則(由於修改了原代碼及調用名稱)
複製代碼

裝飾器(python裏面的動態代理)
本質: 是一個閉包
組成: 函數+實參高階函數+返回值高階函數+嵌套函數+語法糖 = 裝飾器
存在的意義: 在不破壞原有函數和原有函數調用的基礎上,給函數添加新的功能閉包

通用裝飾器寫法:app

複製代碼
 1 def warpper(fn): # fn是目標函數至關於func  2 def inner(*args,**kwargs): # 爲目標函數的傳參  3 '''在執行目標函數以前操做'''  4 ret = fn(*args,**kwargs) # 調用目標函數,ret是目標函數的返回值  5 '''在執行目標函數以後操做'''  6 return ret # 把目標函數返回值返回,保證函數正常的結束  7 return inner  8  9 #語法糖 10 @warpper #至關於func = warpper(func) 11 def func(): 12 pass 13 func() #此時就是執行的inner函數
複製代碼

上邊的場景用裝飾器修改後函數

複製代碼
 1 方式一  2 def game():  3 print('壓子彈')  4 print('槍上膛')  5 print('發射子彈')  6  7 def sight(fn): # fn接收的是一個函數  8 def inner():  9 print('安裝專業狙擊瞄準鏡') 10 fn() #調用傳遞進來的函數 11 print('跑路') 12 return inner #返回函數地址 13 14 game = sight(game) #傳遞game函數到sight函數中 15 game() 16 17 執行步驟 18 第一步定義兩個函數game()爲普通函數,sight()爲裝飾器函數 19 第二步定義game = sight(game)等於把game函數當作參數傳遞給sight(fn)裝飾器函數fn形參 20 第三步執行sight(fn),fn在形參位置,至關於下邊函數game()傳參過來等於fn 21 第四步執行inner函數,而後return把inner函數內存地址當作返回值返回給sight(game) 22 第五步而後執行game(),至關於執行inner函數 23 第六步,執行inner函數,打印'狙擊鏡',執行fn()形參,因爲fn形參等於game函數,因此執行game()函數,打印'壓子彈','上膛','發射子彈' 24 第七步打印'跑路' 25 第八步把打印的結果返回給game() 26 27 方式二 28 def sight(fn): # fn接收的是一個函數 29 def inner(): 30 print('安裝專業狙擊瞄準鏡') 31 fn() #調用傳遞進來的函數 32 print('跑路') 33 return inner #返回函數地址 34 35 @sight #至關於game = sight(game) 36 def game(): 37 print('壓子彈') 38 print('槍上膛') 39 print('發射子彈') 40 game() 41 42 執行步驟 43 第一步執行sight(fn)函數 44 第二步執行@sight,至關於把把game函數與sight裝飾器作關聯 45 第三步把game函數當作參數傳遞給sight(fn)裝飾器函數fn形參 46 第四步執行inner函數,而後return把inner函數內存地址當作返回值返回給@sight 47 第五步執行game()至關至關於執行inner()函數,由於@sight至關於game = sight(game) 48 第六步打印'瞄準鏡 49 第七步執行fn函數,由於fn等於game函數,因此會執行game()函數,打印'壓子彈','上膛','發射子彈'.fn()函數執行完畢 50 第八步打印'跑路' 51 第九步而後把全部打印的結果返回給game() 52 53 結果 54 安裝專業狙擊瞄準鏡 55 壓子彈 56 槍上膛 57 發射子彈 58 跑路
複製代碼

一個簡單的裝飾器實現測試

複製代碼
 1 def warpper(fn):  2 def inner():  3 print('每次執行被裝飾函數以前都要先通過這裏')  4  fn()  5 return inner  6 @warpper  7 def func():  8 print('執行了func函數')  9 func() 10 11 結果 12 每次執行被裝飾函數以前都要先通過這裏 13 執行了func函數
複製代碼

 帶有一個或多個參數的裝飾器this

複製代碼
 1 def sight(fn): #fn等於調用game函數  2 def inner(*args,**kwargs): #接受到的是元組("bob",123)  3 print('開始遊戲')  4 fn(*args,**kwargs) #接受到的全部參數,打散傳遞給user,pwd  5 print('跑路')  6 return inner  7 @sight  8 def game(user,pwd):  9 print('登錄遊戲用戶名密碼:',user,pwd) 10 print('壓子彈') 11 print('槍上膛') 12 print('發射子彈') 13 game('bob','123') 14 結果 15 開始遊戲 16 登錄遊戲用戶名密碼: bob 123 17 壓子彈 18 槍上膛 19 發射子彈 20 跑路
複製代碼

動態傳遞一個或多個參數給裝飾器spa

複製代碼
 1 def sight(fn): #調用game函數  2 def inner(*args,**kwargs): #接受到的是元組("bob",123)  3 print('開始遊戲')  4 fn(*args,**kwargs) #接受到的全部參數,打散傳遞給正常的參數  5 print('跑路')  6 return inner  7 @sight  8 def game(user,pwd):  9 print('登錄遊戲用戶名密碼:',user,pwd) 10 print('壓子彈') 11 print('槍上膛') 12 print('發射子彈') 13 return '遊戲展現完畢' 14 ret = game('bob','123') #傳遞了兩個參數給裝飾器sight 15 print(ret) 16 17 @sight 18 def car(qq): 19 print('登錄QQ號%s'%qq) 20 print('開始戰車遊戲') 21 ret2 = car(110110) #傳遞了一個參數給裝飾器sight 22 print(ret2) 23 結果 24 開始遊戲 25 登錄遊戲用戶名密碼: bob 123 26 壓子彈 27 槍上膛 28 發射子彈 29 跑路 30 None 31 開始遊戲 32 登錄QQ號110110 33 開始戰車遊戲 34 跑路 35 None 36 你會發現這兩個函數執行的返回值都爲None,可是我game定義返回值了return '遊戲展現完畢',卻沒給返回
複製代碼

裝飾器的返回值設計

複製代碼
 1 爲何我定義了返回值,可是返回值仍是None呢,是由於我即便在game函數中定義了return '遊戲展現完畢'  2 可是裝飾器裏只有一個return inner定義返回值,可是這個返回值是返回的inner函數的內存地址的,並非inner  3 函數內部的return因此默認爲None,因此應該定義一個inner函數內部的return返回值,並且也沒有接收返回值的變量,  4 因此要要設置ret = fn(*args,**kwargs)和return ret  5  6 def sight(fn): #調用game函數  7 def inner(*args,**kwargs): #接受到的是元組("bob",123)  8 print('開始遊戲')  9 ret = fn(*args,**kwargs) #接受到的全部參數,打散傳遞給正常的參數 10 print('跑路') 11 return ret 12 return inner 13 @sight 14 def game(user,pwd): 15 print('登錄遊戲用戶名密碼:',user,pwd) 16 print('壓子彈') 17 print('槍上膛') 18 print('發射子彈') 19 return '遊戲展現完畢' 20 ret = game('bob','123') #傳遞了兩個參數給裝飾器sight 21 print(ret) 22 結果 23 開始遊戲 24 登錄遊戲用戶名密碼: bob 123 25 壓子彈 26 槍上膛 27 發射子彈 28 跑路 29 遊戲展現完畢 30 31 32 事例2 33 def wrapper_out(flag): #裝飾器自己的參數 34 def wrapper(fn): #目標函數 35 def inner(*args,**kwargs): #目標函數須要接受的參數 36 if flag == True: 37 print('找第三方問問價格行情') 38 ret = fn(*args,**kwargs) 39 print('買到裝備') 40 return ret 41 else: 42 ret = fn(*args,**kwargs) 43 return ret 44 return inner 45 return wrapper 46 #語法糖,@裝飾器 47 @wrapper_out(True) 48 def func(a,b): #被wrapper裝飾 49 print(a,b) 50 print('開黑') 51 return 'func返回值' 52 abc = func('我是參數1','我是參數2') 53 print(abc) 54 結果 55 找第三方問問價格行情 56 我是參數1 我是參數2 57 開黑 58 買到裝備 59 func返回值
複製代碼

多個裝飾器同用一個函數代理

複製代碼
 1 def wrapper1(fn):  2 def inner(*args,**kwargs):  3 print('wrapper1-1')  4 ret = fn(*args,**kwargs)  5 print('wrapper1-2')  6 return ret  7 return inner  8  9 def wrapper2(fn): 10 def inner(*args,**kwargs): 11 print('wrapper2-1') 12 ret = fn(*args,**kwargs) 13 print('wrapper2-2') 14 return ret 15 return inner 16 17 def wrapper3(fn): 18 def inner(*args,**kwargs): 19 print('wrapper3-1') 20 ret = fn(*args,**kwargs) 21 print('wrapper3-2') 22 return ret 23 return inner 24 @wrapper1 25 @wrapper2 26 @wrapper3 27 def func(): 28 print('我是測試小白') 29 func() 30 結果 31 wrapper1-1 32 wrapper2-1 33 wrapper3-1 34 我是測試小白 35 wrapper3-2 36 wrapper2-2 37 wrapper1-2
相關文章
相關標籤/搜索