裝飾器可以使函數執行前和執行后分別執行其他的附加功能,這種在代碼運行期間動態增加功能的方式,稱之為"裝飾器"(Decorator)
,裝飾器的功能非常強大,裝飾器一般接受一個函數對象作為參數,以對其進行增強,相當于C++中的構造函數,與析構函數。
裝飾器本質上是一個python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象.它經常用于有迫切需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景.裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用.
- 裝飾器本身也是一個函數,其作用是,用于裝飾其他函數.
- 裝飾器是一個閉包函數是嵌套函數,通過外層函數提供嵌套函數的環境
- 裝飾器在權限控制,增加額外功能,如增加記錄日志,緩存處理,發送郵件用的比較多
6.1 無參裝飾器
原函數中不帶參數的裝飾器,如下例子假設:我定義了一個函數lyshark()
,現在想要在不改變原來函數定義的情況下,在函數運行前打印一段話,函數運行后打印另一段話,此時我們可以使用裝飾器的裝飾功能來簡單的實現這個需求.
>>> import os
>>> import sys
>>>
>>> def outer(function):def inner():print("主函數開始執行前,會先執行我!")result=function()print("主函數執行結束后,要在執行我!")return resultreturn inner# (1) @ + 函數名,直接作用在需要裝飾的函數上一行
# (2) 自動執行outer函數并且將下面的函數名lyshark()當做參數傳遞到outer()
# (3) 將outer函數的返回值inner,重新賦值給lyshark()函數
>>> @outer
def lyshark():print("lyshark 的主函數體,裝飾器在裝飾我(*^_^*)")return "check ok"#==調用并執行函數,結果如下==========================================
>>> ret=lyshark()
主函數開始執行前,會先執行我!
lyshark 的主函數體,裝飾器在裝飾我(*^_^*)
主函數執行結束后,要在執行我!>>> print("lyshark()函數的返回值: ",ret)
lyshark()函數的返回值: check
上方代碼的執行流程是這樣的,步驟如下:
- 1.當我們調用lyshark()函數時,會自動檢查lyshark()函數上是否有裝飾器
- 2.如果有則將lyshark()函數的指針,傳遞給裝飾器outer(function)
- 3.outer(function)接到指針后,執行嵌套函數內的inner(),則先執行print打印一段話
- 4.由于lyshark()函數指針,傳遞給了function變量,執行function()則相當于執行lyshark()
- 5.接著最后一步執行打印一段結束的話,并執行返回,返回inner
6.2 有參裝飾器
原函數帶一個參數的裝飾器: 我們在以上的案例中,給裝飾器添加一個參數,并在內部使用這個參數.
>>> import os
>>> import sys
>>>
>>> def outer(function):def inner(args):print("主函數開始執行前,會先執行我!")ret=function(args)print("主函數執行結束后,要在執行我!")return retreturn inner>>> @outer
def lyshark(args):print(args)return 0#==調用并執行函數,結果如下==========================================
>>> ret=lyshark("hello world!")
主函數開始執行前,會先執行我!
hello world!
主函數執行結束后,要在執行我!>>> print("lyshark 的返回值是:",ret)
lyshark() 函數的返回值是: 0
原函數帶兩個參數的裝飾器: 接下來繼續演示一下,帶有兩個參數的裝飾器,3個4個,以此類推.
>>> import os
>>> import sys
>>>
>>>
>>> def outer(function):def inner(x,y):print("主函數開始執行前,會先執行我!")ret=function(x,y)print("主函數執行結束后,要在執行我!")return retreturn inner>>> @outer
def lyshark(x,y):print(x,y)return 0#==調用并執行函數,結果如下==========================================
>>> ret=lyshark("Hello","LyShark")
主函數開始執行前,會先執行我!
Hello LyShark
主函數執行結束后,要在執行我!>>> print("lyshark() 函數的返回值是:",ret)
lyshark() 函數的返回值是: 0
傳遞一個萬能參數: 裝飾器也可傳遞一個萬能參數,通過此參數傳遞列表字典等.
>>> import os
>>> import sys
>>>
>>> def outer(function):def inner(*args,**kwargs):print("主函數開始執行前,會先執行我!")ret=function(*args,**kwargs)print("主函數執行結束后,要在執行我!")return retreturn inner>>> @outer
def lyshark(*args):print(args)return 0#==調用并執行函數,結果如下==========================================
>>> num=[1,2,3,4,5]
>>> ret=lyshark(num)
主函數開始執行前,會先執行我!
([1, 2, 3, 4, 5],)
主函數執行結束后,要在執行我!
>>>
>>> print("lyshark() 函數的返回值是:",ret)
lyshark() 函數的返回值是: 0#==調用并執行函數,結果如下==========================================
@outer
def lyshark_kw(*args,**kwargs):print(args,kwargs)return 0num=[1,2,3,4,5]
kw={"1001":"admin","1002":"guest"}
ret=lyshark_kw(num,kw)
一次使用兩個裝飾器裝飾函數: 如果一個裝飾器不夠用的話,我們可以使用兩個裝飾器,首先將函數與內層裝飾器結合然后在與外層裝飾器相結合,要理解使用@語法
的時候到底執行了什么,是理解裝飾器的關鍵.
>>> import os
>>> import sys
>>>
>>> def outer2(function2):def inner2(*args,**kwargs):print("裝飾器2--->【開始】")ret=function2(*args,**kwargs)print("裝飾器2--->【結束】")return retreturn inner2>>> def outer1(function1):def inner1(*args,**kwargs):print("裝飾器1--->【開始】")ret=function1(*args,**kwargs)print("裝飾器1--->【結束】")return retreturn inner1@outer2
@outer1
def lyshark():print("lyshark 函數被執行了")#==調用并執行函數,結果如下==========================================
>>> lyshark()
裝飾器2--->【開始】
裝飾器1--->【開始】
lyshark 函數執行了
裝飾器1--->【結束】
裝飾器2--->【結束】#==調用并執行函數,結果如下==========================================
@outer1
@outer2
def lyshark_and():print("lyshark_and 函數被執行了")>>> lyshark_and()
裝飾器1--->【開始】
裝飾器2--->【開始】
lyshark_and 函數執行了
裝飾器2--->【結束】
裝飾器1--->【結束】
6.3 帶參裝飾器
前面的裝飾器本身沒有帶參數,如果要寫一個帶參數的裝飾器怎么辦,那么我們就需要寫一個三層的裝飾器,而且前面寫的裝飾器都不太規范,下面來寫一個比較規范帶參數的裝飾器,下面來看一下代碼,大家可以將下面的代碼自我運行一下.
給裝飾器本身添加參數: 接下來我們將給裝飾器本身添加一些參數,使其能夠實現參數傳遞.
>>> import functools
>>> import sys
>>>
>>> def lyshark(temp=""): #指定裝飾器默認參數def decorator(function): #定義裝飾器@functools.wraps(function) #使被裝飾的裝飾器的函數名不改變def wrapper(*args,**kwargs):print("主函數開始執行前,會先執行我!")print("{}:{}".format(temp,function.__name__)) #這里調用了裝飾器temp變量ret=function(*args,**kwargs)print("主函數執行結束后,要在執行我!")return retreturn wrapperreturn decorator#==調用并執行函數,結果如下==========================================
>>> #如果不給裝飾器加參數,那么這個裝飾器將使用默認參數 temp="",來填充
>>> @lyshark()
def test(x):print(x+100)>>> test(100)
主函數開始執行前,會先執行我!
:test #這里由于沒有傳遞參數則第一項為空,第二項是函數名稱`function.__name__`取出的
主函數執行結束后,要在執行我!#==調用并執行函數,結果如下==========================================
>>> #下面是給裝飾器一個參數,將不是用默認參數 temp="",將變成 temp="LyShark"
>>> @lyshark("LyShark")
def test(x):print(x+100)>>> test(100)
主函數開始執行前,會先執行我!
LyShark:test
主函數執行結束后,要在執行我!
給裝飾器本身添加參數: 接下來我們將給裝飾器本身添加兩個參數,使其能夠傳遞多個參數.
>>> import sys
>>> import os
>>>
>>> def lyshark(x="Hello",y="LyShark"):def decorator(function):def wrapper():print("主函數執行前,應先執行我!")print(x,y)ret=function()print("主函數執行后,要執行我!")return retreturn wrapperreturn decorator#==調用并執行函數,結果如下==========================================
>>> #使用默認參數的裝飾器:此時 x="Hello" y="LyShark"
>>> @lyshark()
def test():print("我是test(),主函數,裝飾器在裝飾我")>>> test()
主函數執行前,應先執行我!
Hello LyShark
我是test(),主函數,裝飾器在裝飾我
主函數執行后,要執行我!#==調用并執行函數,結果如下==========================================
>>> #給裝飾器指定參數:此時 x="My Name Is :" y="LyShark"
>>> @lyshark("My Name Is :","LyShark")
def test():print("我是test(),主函數,裝飾器在裝飾我")>>> test()
主函數執行前,應先執行我!
My Name Is : LyShark
我是test(),主函數,裝飾器在裝飾我
主函數執行后,要執行我!
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/63fd2e5b.html
版權聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協議。轉載請注明出處!