溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

python如何使用裝飾器

發(fā)布時間:2020-09-24 12:33:58 來源:億速云 閱讀:114 作者:Leah 欄目:編程語言

python如何使用裝飾器?針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

1、裝飾器的理解

裝飾器是將一個函數(shù)鑲嵌在另一個函數(shù)中進行重復(fù)使用的目的,不改變其結(jié)構(gòu),增加函數(shù)的使用方式,但是不用寫過多冗余的代碼;

裝飾器本質(zhì)上是一個Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數(shù)對象。

通常用到的功能:1.引入日志;2.函數(shù)執(zhí)行時間統(tǒng)計;3.執(zhí)行函數(shù)前預(yù)備處理;4.執(zhí)行函數(shù)后清理功能;5.權(quán)限校驗;6.緩存

2、實現(xiàn)原理與通用寫法

咱們可以從一個簡單的記錄函數(shù)運行時間的簡單裝飾器,舉一反三,推導(dǎo)出一個通用的裝飾器寫法

import time
 
def timer(func):
    '''
    記錄方法運行時間的裝飾器
    :param func: 方法
    :return:函數(shù)對象
    '''
    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回去,否則,默認(rèn)返回值為 None,一般默認(rèn)都返回
 
    return deco
 
 
@timer
def test(parameter):
    print("test is running!")
    time.sleep(1)
    return "Returned value" # 該函數(shù)有返回值,所以需要在 裝飾器中的 deco 方法中 寫返回值
 
 
t = test('aa')
print(t)

這是一個很簡單的通用的記錄時間的裝飾器,從而推導(dǎo)出一個通用的裝飾器寫法:

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

ok 裝飾器到此可以完事了,一般情況下都能滿足需求了,網(wǎng)上看那么多原理,有點兒浪費時間,我偏向?qū)嵅傩?實在不喜歡啰嗦那么多,就是干。

當(dāng)然在開發(fā)過程中, 我們可能會遇到一些特殊情況,比如參數(shù)問題

1、給裝飾器函數(shù)代參數(shù)(通用)

2、將執(zhí)行函數(shù)的參數(shù)拆分計算等(比如:1000w的數(shù)據(jù),拆分成100份執(zhí)行等)(定制)

那就按順序來

1、寫一個代參數(shù)的裝飾器

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")

發(fā)現(xiàn):就是在上面的通用的模板上又套了一層?。?!,然后拿到里面的參數(shù)即可! so easy!!!

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

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

可以看出來,這個的定制性稍微高點,不通用,但是我們實現(xiàn)了我們的需求,所以,我們最應(yīng)該理解并學(xué)會的是怎么用!??!

可以看出來,這個的定制性稍微高點,不通用,但是我們實現(xiàn)了我們的需求,所以,我們最應(yīng)該理解并學(xué)會的是怎么用!?。?/p>

下面在介紹一下基于類實現(xiàn)的裝飾器,那問題來了,我是實戰(zhàn)派,我并沒有用類裝飾器的需求,所以,當(dāng)個大盜吧,以后用到了不至于瞎找了?。?!

裝飾器函數(shù)其實是這樣一個接口約束,它必須接受一個callable對象作為參數(shù),然后返回一個callable對象。在Python中一般callable對象都是函數(shù),但也有例外。只要某個對象重載了__call__()方法,那么這個對象就是callable的。

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

像__call__這樣前后都帶下劃線的方法在Python中被稱為內(nèi)置方法,有時候也被稱為魔法方法。重載這些魔法方法一般會改變對象的內(nèi)部行為。上面這個例子就讓一個類對象擁有了被調(diào)用的行為。

回到裝飾器上的概念上來,裝飾器要求接受一個callable對象,并返回一個callable對象(不太嚴(yán)謹(jǐn),詳見后文)。

那么用類來實現(xiàn)也是也可以的。我們可以讓類的構(gòu)造函數(shù)__init__()接受一個函數(shù),然后重載__call__()并返回一個函數(shù),也可以達(dá)到裝飾器函數(shù)的效果。

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)

帶參數(shù)的類裝飾器

如果需要通過類形式實現(xiàn)帶參數(shù)的裝飾器,那么會比前面的例子稍微復(fù)雜一點。那么在構(gòu)造函數(shù)里接受的就不是一個函數(shù),而是傳入的參數(shù)。通過類把這些參數(shù)保存起來。

然后在重載__call__方法是就需要接受一個函數(shù)并返回一個函數(shù)。

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

關(guān)于python如何使用裝飾器問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI