python中的裝飾器的使用實戰

daxuesheng發表於2021-09-11

python中的裝飾器的使用實戰

1、裝飾器的理解

裝飾器是將一個函式鑲嵌在另一個函式中進行重複使用的目的,不改變其結構,增加函式的使用方式,但是不用寫過多冗餘的程式碼;

裝飾器本質上是一個Python函式,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外功能,裝飾器的返回值也是一個函式物件。

通常用到的功能:1.引入日誌;2.函式執行時間統計;3.執行函式前預備處理;4.執行函式後清理功能;5.許可權校驗;6.快取

2、實現原理與通用寫法

我們們可以從一個簡單的記錄函式執行時間的簡單裝飾器,舉一反三,推匯出一個通用的裝飾器寫法

import time
 
def timer(func):
    '''
    記錄方法執行時間的裝飾器
    :param func: 方法
    :return:函式物件
    '''
    def deco(*args, **kwargs):
        startTime = time.time()
        f = func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime) * 1000
        print("time is %d ms" % msecs)
        return f # 如果 func 有返回值得話,需要在此return回去,否則,預設返回值為 None,一般預設都返回
 
    return deco
 
 
@timer
def test(parameter):
    print("test is running!")
    time.sleep(1)
    return "Returned value" # 該函式有返回值,所以需要在 裝飾器中的 deco 方法中 寫返回值
 
 
t = test('aa')
print(t)

這是一個很簡單的通用的記錄時間的裝飾器,從而推匯出一個通用的裝飾器寫法:

def func_name(func): # 自定義裝飾器函式名
    def deco(*args, **kwargs): # 將所有引數原封不動的進行傳遞
        print("在這個分割線之上寫函式執行前的操作")
#         -----------分割線-----------
        f = func(*args, **kwargs)
#         -----------分割線-----------
        print("在這個分割線之後,return之前,寫函式執行後的操作")
        return f # 如果 func 有返回值得話,需要在此return回去,否則,預設返回值為 None,一般預設都返回
 
    return deco
 
 
@func_name
def test(parameter):  # 8
    print("test is running!")
    time.sleep(1)
    return "Returned value" # 該函式有返回值,所以需要在 裝飾器中的 deco 方法中 寫返回值
 
 
t = test('aa')
print(t)

ok 裝飾器到此可以完事了,一般情況下都能滿足需求了,網上看那麼多原理,有點兒浪費時間,我偏向實操型,實在不喜歡囉嗦那麼多,就是幹。

當然在開發過程中, 我們可能會遇到一些特殊情況,比如引數問題

1、給裝飾器函式代引數(通用)

2、將執行函式的引數拆分計算等(比如:1000w的資料,拆分成100份執行等)(定製)

那就按順序來

1、寫一個代引數的裝飾器

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print("[{level}]: enter function {func}()".format(level=level, func=func.__name__))
            return func(*args, **kwargs)
        return inner_wrapper
    return wrapper
 
@logging(level='INFO')
def say(something):
    print("say {}!".format(something))
 
# 如果沒有使用@語法,等同於
# say = logging(level='INFO')(say)
 
@logging(level='DEBUG')
def do(something):
    print("do {}...".format(something))
 
if __name__ == '__main__':
    say('hello')
    do("my work")

發現:就是在上面的通用的模板上又套了一層!!!,然後拿到裡面的引數即可! so easy!!!

2、寫一個引數拆分的裝飾器,這個就稍微有點定製型了,不能像上面的一樣通用了,舉個 栗子:

def func_name(func): # 自定義裝飾器函式名
    def deco(*args, **kwargs): # 將所有引數原封不動的進行傳遞
        print(args[0])
        f_list = []
        for i in range(0,args[0],100000):
            print(i)
            f_list.append(func(i))
#         f_list # 這兒應該按照既定規則,繼續對這個結果進行拼接,如果是寫檔案、入庫等操作,可以不用return
        return f_list   # 這兒如果有返回值得話,應該是
    
    return deco
 
@func_name
def test(parameter):  # 8
    print("test is running!")
    time.sleep(1)
    return "Returned value" # 該函式有返回值,所以需要在 裝飾器中的 deco 方法中 寫返回值
 
 
t = test(1000000)
print(t)

可以看出來,這個的定製性稍微高點,不通用,但是我們實現了我們的需求,所以,我們最應該理解並學會的是怎麼用!!!

可以看出來,這個的定製性稍微高點,不通用,但是我們實現了我們的需求,所以,我們最應該理解並學會的是怎麼用!!!

下面在介紹一下基於類實現的裝飾器,那問題來了,我是實戰派,我並沒有用類裝飾器的需求,所以,當個大盜吧,以後用到了不至於瞎找了!!!

裝飾器函式其實是這樣一個介面約束,它必須接受一個callable物件作為引數,然後返回一個callable物件。在Python中一般callable物件都是函式,但也有例外。只要某個物件過載了__call__()方法,那麼這個物件就是callable的。

class Test():
    def __call__(self):
        print 'call me!'
t = Test()
t()  # call me

像__call__這樣前後都帶下劃線的方法在Python中被稱為內建方法,有時候也被稱為魔法方法。過載這些魔法方法一般會改變物件的內部行為。上面這個例子就讓一個類物件擁有了被呼叫的行為。

回到裝飾器上的概念上來,裝飾器要求接受一個callable物件,並返回一個callable物件(不太嚴謹,詳見後文)。

那麼用類來實現也是也可以的。我們可以讓類的建構函式__init__()接受一個函式,然後過載__call__()並返回一個函式,也可以達到裝飾器函式的效果。

class logging(object):
    def __init__(self, func):
        self.func = func
 
    def __call__(self, *args, **kwargs):
        print "[DEBUG]: enter function {func}()".format(
            func=self.func.__name__)
        return self.func(*args, **kwargs)
@logging
def say(something):
    print "say {}!".format(something)

帶引數的類裝飾器

如果需要透過類形式實現帶引數的裝飾器,那麼會比前面的例子稍微複雜一點。那麼在建構函式里接受的就不是一個函式,而是傳入的引數。透過類把這些引數儲存起來。

然後在過載__call__方法是就需要接受一個函式並返回一個函式。

class logging(object):
    def __init__(self, level='INFO'):
        self.level = level
        
    def __call__(self, func): # 接受函式
        def wrapper(*args, **kwargs):
            print "[{level}]: enter function {func}()".format(
                level=self.level,
                func=func.__name__)
            func(*args, **kwargs)
        return wrapper  #返回函式
 
@logging(level='INFO')
def say(something):
    print "say {}!".format(something)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4692/viewspace-2837147/,如需轉載,請註明出處,否則將追究法律責任。

相關文章