python裝飾器之原理實現,作用,與例程
裝飾器的功能
當需要對一段寫好的程式碼新增一段新的需求的時候的時候我們就可以用裝飾器實現。
def set_func(func):
def call_funct():
print("---這是許可權驗證1---")
print("---這是許可權驗證2——————")
func()
return call_funct
@set_func
def test_1():
print("----test1----")
test_1()
對test_1函式新增驗證1和驗證2的功能,需要設計一個閉包,閉包的外部引數傳遞的是函式的引用,在內部函式裡面新增需要新增的功能。比如以上這段程式碼,我們在test_1函式前面新增上@set_func這個裝飾器,在呼叫test_1函式的時候,我們就會按照call_funct函式裡面的順序執行。
裝飾器的原理
def set_func(func):
def call_funct():
print("---這是許可權驗證1---")
print("---這是許可權驗證2——————")
func()
return call_funct
#@set_func
def test_1():
print("----test1----")
test_1 = set_func(test_1)
test_1()
#test_1()
裝飾器的原理其實就是函式引用的傳遞,在閉包外部傳遞函式的引用,內部函式執行完“這是許可權驗證1”和“這是許可權驗證2”之後,就會把外部函式傳遞的函式引用引數拿過來執行
如果把@set_func這個裝飾器去掉之後呢
不難理解,因為內部函式執行了兩個驗證之後,我們的函式的引用是被裝飾器修飾過的,所以我們會在執行一下兩個驗證,最後再執行test_1函式的功能
裝飾器統計函式執行時間
import time
def set_func(func):
def call_funct():
start_time=time.time()
func()
stop_time=time.time()
print("執行時間是%f"%(start_time-stop_time))
return call_funct
@set_func
def test_1():
print("----test1----")
for i in range(1000):
pass
test_1()
對有引數無返回值的函式進行修飾
def set_func(func):
def call_funct(num):
print("---這是許可權驗證1---")
print("---這是許可權驗證2——————")
func(num)
return call_funct
@set_func
def test_1(num):
print("----test1----%d"%num)
test_1(10000)
對有引數無返回值的參書進行修飾的時候,修飾的函式有幾個引數,閉包的內部函式就需要有幾個引數,函式地址傳遞給外部函式,函式實參傳遞給內部引數
不定長引數的函式裝飾器
可以直接在閉包的內部函式裡寫不定長引數,當然也可以按照呼叫函式的形參格式進行傳遞。
def set_func(func):
print("開啟裝飾器")
def call_func(*args,**kwargs):
print("11111")
print("22222")
func(*args,**kwargs)
return call_func
@set_func
def test(num,*args,**kwargs):
print("test---%d"%num)
print("test---",args)
print("test---",kwargs)
test(100)
test(100,200)
test(100,200,300,mm=500)
以上就是按照不定長引數進行傳遞的。執行結果如下
對帶有返回值的函式進行裝飾,通用裝飾器
首先觀察一段錯誤例程
def set_func(func):
print("開啟裝飾器")
def call_func(*args,**kwargs):
print("11111")
print("22222")
func(*args,**kwargs)
return call_func
@set_func
def test(num,*args,**kwargs):
print("test---%d"%num)
print("test---",args)
print("test---",kwargs)
return "OK"
ret = test(100)
print(ret)
test(100)去呼叫閉包函式,執行完“開啟裝飾器 111 222”之後,去執行test函式,test函式有返回值,但是call_func函式沒有返回值,所以這個返回值並沒有被ret接收到,所以打印出來的結果是None
正確的做法是call_func這個內部函式返回test函式的返回值,正確例程如下:
def set_func(func):
print("開啟裝飾器")
def call_func(*args,**kwargs):
print("11111")
print("22222")
return func(*args,**kwargs)
return call_func
@set_func
def test(num,*args,**kwargs):
print("test---%d"%num)
print("test---",args)
print("test---",kwargs)
return "OK"
ret = test(100)
print(ret)
那麼如果被裝飾函式沒有返回值,但是裝飾器有返回值會出現什麼情況呢
def set_func(func):
print("開啟裝飾器")
def call_func(*args,**kwargs):
print("11111")
print("22222")
return func(*args,**kwargs)
return call_func
@set_func
def test(num,*args,**kwargs):
print("test---%d"%num)
print("test---",args)
print("test---",kwargs)
return "OK"
@set_func
def a():
pass
ret = test(100)
print(ret)
ret2=a()
print(ret2)
這個時候ret2會返回None,所有不會對函式有任何影響,故,以上例程的內部函式就是通用裝飾器。
多個裝飾器對同一個函式進行修飾
def set_func(func):
print("開啟裝飾器1111")
def call_func():
print("11111")
return func()
return call_func
def add_qx(func):
print("開啟裝飾器222")
def call_func():
print("222")
return func()
return call_func
@set_func
@add_qx
def test():
print("----test----")
test()
首先程式是可以執行的,但是執行順序呢,簡單來說,開啟裝飾器的順序是從下到上,執行內部函式的時候,由上到下,結果如下:
類的裝飾器
class Test(object):
def __init__(self,func):
self.func=func
def __call__(self, *args, **kwargs):
print("這裡是裝飾器新增的功能")
return self.func()
@Test
def get_str():
return "zhadf"
print(get_str())
執行結果:
裝飾器帶引數
def set_level(level_num):
def set_func(func):
def call_func(*args, **kwargs):
# level = args[0]
if level_num == 1:
print("許可權驗證1")
elif level_num == 2:
print("許可權驗證2")
return func()
return call_func
return set_func
@set_level(1)
def test1():
print("test1")
return "OK"
@set_level(2)
def test2():
print("test2")
return "OK"
test1()
test2()
-
需要在閉包外層再定義一個函式,這個函式用來接受裝飾器的引數。效果如上。
----------------------------------------------------- -
github:https://github.com/zhangyuespec/mini_web/tree/master/mini_web框架/裝飾器