Table of Contents
01介紹
不會裝飾器,不能說會python喲
def foo(): print('foo')
foo#函數名指向函數 foo()#執行函數
foo=lambda x: x+1#foo變量名被指向匿名函數 foo()#執行lambda表達式 |
Py把函數名、方法名、變量名都當作變量名對待,所以不能相同
只要有相同的,則用的時候就是最後定義的一個
需求:基礎平臺部門寫功能,業務部門調用(容易被裁員)
讓業務部門調用的時候加權限驗證,不加不能調用功能函數
#法1:冗餘過多 def f1(): #權限驗證 print('f1') def f2(): #權限驗證 print('f2')
f1() f2() |
#法2 def check_login(): #權限驗證 pass
def f1(): check_login() print('f1') def f2(): check_login() print('f2')
f1() f2() |
開放封閉原則規定已經實現的功能代碼不能修改(封閉),但可以擴展(開放)
裝飾器效果
def set_func(func): def call_func(): print("權限驗證") func() return call_func
@set_func#在函數執行前加代碼 def test1(): #set_func()#類似效果 print("test1")
test1() |
02實現裝飾器
裝飾器實現過程:
def set_func(func):#傳入的指針是test1 def call_func(): print("權限驗證") func() return call_func
def test1(): print("test1")
ret=set_func(test1)#ret得到call_func指針 ret()#執行print("權限驗證")和調用func()即指向test1 |
test1=set_func(test1)#不用修改調用的函數名,重新修改指針 test1()#執行print("權限驗證")和func()即test1 |
@set_func可看作一種簡略寫法
@set_func#等價於test1=set_func(test1) def test1(): print("test1") |
由於裝飾器(修飾器)的閉包實現過程是整個調用原函數,新加的功能只能在原函數調用之前或之後執行,不能在執行到一半時插入
03裝飾器的作用 對有參函數的裝飾
裝飾器能夠實現對功能的擴展,同時不改變調用的函數名
作用:如計算函數運行的時間
Import time time.time() |
1508763827.814883 |
整數部分是1970年到現在秒數(Unix時間戳)
import time def set_func(func):#傳入的指針是test1 def call_func(): start_time=time.time() func() stop_time=time.time() print("all time is %f" % (stop_time - start_time)) return call_func
@set_func#等價於test1=set_func(test1) def test1(): print("test1") test1()#執行print("權限驗證")和func()即test1 |
對無參數、返回值函數最簡單
對有參數無返回值函數裝飾
def set_func(func):#傳入的指針是test1 def call_func(a):#2.接收到100 print("權限驗證") func(a)#3.100作爲實參調用test1 return call_func
@set_func def test1(num): print("test1:%d" % num)
test1(100)#1.100給了call_fun |
04 裝飾多個函數
同一個裝飾器裝飾多個函數
def set_func(func): def call_func(a): print("權限驗證") func(a) return call_func
@set_func def test1(num): print("test1:%d" % num)
@set_func def test1(num): print("test2:%d" % num)
test1(100)#1.100給了call_fun test2(200) |
兩次裝飾分別創建多個閉包
test1、test2分別指向另一個函數,該函數分別嵌套原本的func(test1、test2)
裝飾器@寫完就已經裝飾了,而不需要等調用test1()
def set_func(func): print("開始裝飾") def call_func(a): print("權限驗證") func(a) return call_func
@set_func def test1(num): print("test1:%d" % num)
#test1(100) |
test1未運行 終端打印出:開始裝飾 |
05不定長參數的函數裝飾
不定長參數的即便使用:
def test1(num,*args,**kwargs): print("test1:%d" % num) print("test1:", args)#作爲元組打印 print("test1:", kwargs)#作爲元組打印
test1(1,2,3,mm=1) |
其中mm=1爲關鍵字參數
輸出結果:
test1:1 test1:(2,3) test1:{‘mm’:1} |
使用解釋器:
def set_func(func): print("開始裝飾") def call_func(*args,**kwargs):#num1在args中以元組保存 print("權限驗證") func(*args,**kwargs)#傳遞處也要帶*,*代表對元組拆包,**代表對字典拆 return call_func
@set_func def test1(num,*args,**kwargs):#*代表多餘的普通參數給args,**代表關鍵字參數給kwargs print("test1:%d" % num) print("test1:", args) print("test1:", kwargs)
test1(1,2,3,mm=1)#調用call_func |
傳遞可變參數時不能寫成func(args,kwargs)否則只傳遞了一個元組一個字典
06 返回值函數裝飾 通用裝飾器
def set_func(func): print("開始裝飾") def call_func(*args,**kwargs):#num1在args中以元組保存 print("權限驗證") return func(*args,**kwargs)#將接收到的返回值傳遞 return call_func
@set_func def test1(num,*args,**kwargs):#*代表多餘的普通參數給args,**代表關鍵字參數給kwargs print("test1:%d" % num) print("test1:", args) print("test1:", kwargs) return "ok"#誰調用的test1就向誰返回
ret=test1(1)#調用call_func print(ret) |
無返回值函數可適用:
一般接收無返回值函數,拿到的都是none
使用該裝飾器裝飾無返回值的函數,return none結果也一樣是none
多返回值可適用:
多返回值時返回元組
def set_func(func): print("開始裝飾") def call_func(*args,**kwargs): print("權限驗證") return func(*args,**kwargs) return call_func
@set_func def test1(num,*args,**kwargs): print("test1:%d" % num) print("test1:", args) print("test1:", kwargs) return "ok","2"#多返回值時返回元組
ret=test1(1)#調用call_func print(ret) |
通用裝飾器主要有3點:
- call_func(*args,**kwargs)接收可變參數
- func(*args,**kwargs)傳遞實參時拆包
- return func將接收到的返回值傳遞
07多個裝飾器對同一個函數裝飾
def add_verify1(func): print("開始1裝飾") def call_func(*args,**kwargs):#num1在args中以元組保存 print("裝飾1") return func(*args,**kwargs)#將接收到的返回值傳遞 return call_func def add_verify2(func): print("開始2裝飾") def call_func(*args,**kwargs):#num1在args中以元組保存 print("裝飾2") return func(*args,**kwargs)#將接收到的返回值傳遞 return call_func
@add_verify1 @add_verify2 def test1(): print("test1")
test1() |
輸出結果
開始2裝飾 開始1裝飾 裝飾1 裝飾2 |
裝飾與執行順序不同
裝飾add_verify1時,將add_verify2包括以下當成整個函數,需要先裝飾下面的,下面的就變成新閉包
@add_verify1#等價於test1=add_verify1(test1),此時的test1指向已經改變 @add_verify2#等價於test1=add_verify2(test1) def test1(): print("test1") |
08應用
對原函數返回值加"<h1></h1>"標籤
def set_func_1(func): def call_func(*args,**kwargs): return "<h1>"+func(*args,**kwargs)+"</h1>" return call_func
@set_func_1 def get_str(): return "haha"
print(get_str()) |
再加"<td></td>"標籤
def set_func_1(func): def call_func(*args,**kwargs): # print("裝飾1") return "<h1>"+func(*args,**kwargs)+"</h1>" return call_func def set_func_2(func): def call_func(*args,**kwargs): # print("裝飾1") return "<td>"+func(*args,**kwargs)+"</td>" return call_func
@set_func_1 @set_func_2 def get_str(): return "haha"
print(get_str()) |
輸出結果:
<h1><td>haha</td></h1> |
因爲h1裝飾的在最上面,執行的時候再遞歸調用td的裝飾
09類裝飾器
用類對函數裝飾
class Test(object): def __init__(self,func):#帶self的可算作實例方法,但肯定不是類方法 self.func=func#存下func引用 def __call__(self): print("裝飾器功能") return self.func()#指向get_str
@Test#get_str=Test(get_str)創建Test類的實例對象 def get_str(): return "haha"
print(get_str())#調用時調用的是類裝飾器的call方法 |
若要改成通用裝飾器:get_str()調用類的call方法,所以參數也在call接收
類方法,靜態方法可以通過類名調用,如@Test.jingtaifangfa