Python學習—裝飾器

來源:互聯網
上載者:User

標籤:修改   函數對象   模仿   parameter   寫在前面   新聞   可變   tar   原函數   

裝飾器

裝飾器實際上就是為了給某程式增添功能,但該程式已經上線或已經被使用,那麼就不能大批量的修改原始碼,這樣是不科學的也是不現實的,因為就產生了裝飾器,使得其滿足:
(1).不能修改被裝飾的函數的原始碼
(2).不能修改被裝飾的函數的調用方式
(3).滿足(1)、(2)的情況下給程式增添功能
實現:
我們寫一個嵌套函數,在內建函式中添加新功能新內容,然後調用原函數,再在外部函數return這個內建函式。由於函數也是一個對象,而且函數對象可以被賦值給變數,所以我們調用這個嵌套函數,把傳回值(返回的是一個函數)賦給一個和原函數同名的變數,通過變數也能調用該函數。這樣就實現了不改變原函數代碼增強其功能。

舉個例子:
已經有一個函數login()實現的登陸功能,我想給它添加一些新內容:

def desc(fun):    def add_info():        print(‘寫在前面.......‘)        fun()        print(‘寫在後面........‘)    return add_infodef login():    print(‘login............‘)login = desc(login)login()

運行:

其實這就是一個裝飾器的雛形了。但是在使用這個裝飾器裝飾的時候,需要在每個函數前面加上這樣一句代碼:

    function = desc(function)

顯然有些麻煩,Python提供了一種文法糖來代替它:

@裝飾器名    #注意要把這句代碼放到原函數前

代碼修改如下,會時同樣的效果且更加簡潔:

def desc(fun):    def add_info():        print(‘寫在前面.......‘)        fun()        print(‘寫在後面........‘)    return add_info@descdef login():    print(‘login............‘)login()

用裝飾器來實現計時功能:
計算在字串串連時,+和join那個效率更高。

import random,string,timeli = [random.choice(string.ascii_letters) for i in range(1000)]    #這裡用了1000個字元來比較def desc(fun):    def wrapper():        start_time = time.time()        fun()        end_time = time.time()        print(‘%.8f‘ %(end_time-start_time))    return wrapper@descdef a_add():    st = ‘‘    for i in li:        st += (i+‘,‘)    return st@descdef b_add():    return ‘,‘.join(li)a_add()b_add()

運行:

顯然,此時join方式效率要高於串連符+
可以看到以上內容中函數沒有參數,即是無參函數裝飾器。

裝飾有參函數

用裝飾器來實現判斷變數類型:
對函數的傳入參數進行檢查,參數符合要求(整數)正常調用函數,不符合要求則報錯。

import functools   #匯入functools包def required_int(fun):    @functools.wraps(fun)   #functools包中的wraps函數保證原函數的資訊不因裝飾器而改變。    def wrapper(*args):    #可變參數        for i in args:            if not str(i).isdigit():    #或者用isinstance(i,int)更加簡潔                print(‘參數必須是整數...‘)                return None                break        else:            res=fun(*args)            return res    return wrapper@required_intdef add_num(*args):   #參數為可變參數    sum = 0    for i in args:        sum += i    return sumprint(‘參數合法輸出結果:‘)print(add_num(2,3,45,6))print(‘參數不合法輸出結果:‘)print(add_num(3,4,5,‘e‘))

運行:

模仿部落格邏輯功能:
登陸後才能寫部落格,瀏覽部落格不需要登陸。

import functoolslogin_users = [‘admin‘,‘root‘]     #已經登陸的使用者def is_login(fun):    @functools.wraps(fun)    def wrapper(*args,**kwargs):    #可變參數加關鍵字參數        if kwargs.get(‘name‘) in login_users:            res = fun(*args,**kwargs)        else:            res = login()        return res    return wrapperdef login():    return ‘請先登陸.......‘@is_logindef writtelog(name):    return ‘寫部落格日之.....‘def viewnews():     return ‘看文章新聞.....‘print(writtelog(name=‘root‘))print(writtelog(name=‘root11‘))print(viewnews())

運行:

多個裝飾器的使用

模仿只有admin使用者在登陸後才能進入後台:

import functoolslogin_users = [‘admin‘,‘root‘]     #已經登陸的使用者def is_admin(fun):    @functools.wraps(fun)    def wrapper(*args,**kwargs):        if kwargs.get(‘name‘)==‘admin‘:            res = fun(*args,**kwargs)        else:            res = ‘沒有許可權....‘        return res    return wrapperdef is_login(fun):    @functools.wraps(fun)    def wrapper(*args,**kwargs):        if kwargs.get(‘name‘) in login_users:            res = fun(*args,**kwargs)        else:            res = login()        return res    return wrapperdef login():    return ‘請先登陸.......‘@is_login@is_admindef houtai(name):    return ‘進入後台.....‘print(houtai(name=‘admin‘))print(houtai(name=‘root‘))print(houtai(name=‘uuuu‘))

運行:

這裡需要注意的是,能用一個裝飾器盡量不要使用多個裝飾器。
當有多個裝飾器時,從上往下依次調用裝飾器,真實的wrapper內容時從上到下執行的。

帶參數的裝飾器

一個裝飾器,對不同的函數有不同的裝飾。那麼就需要知道對哪個函數採取哪種裝飾。因此,就需要裝飾器帶一個參數來標記一下。
將參數傳入,我們需要再加一層函數嵌套,來傳遞裝飾器的參數。
舉栗子:
對兩個不同的函數計時:

import timedef timer(parameter):    #參數parameter傳入調用的函數的名字    def outer_wrapper(func):        def wrapper(*args, **kwargs):            if parameter == ‘task1‘:                start_time = time.time()                func(*args, **kwargs)                stop_time = time.time()                print("the task1 run time is :", stop_time - start_time)            elif parameter == ‘task2‘:                start_time = time.time()                func(*args, **kwargs)                stop_time = time.time()                print("the task2 run time is :", stop_time - start_time)        return wrapper    return outer_wrapper@timer(parameter=‘task1‘)def task1():    time.sleep(2)    print("in the task1")@timer(parameter=‘task2‘)def task2():    time.sleep(2)    print("in the task2")task1()task2()

運行:

判斷函數傳入參數升級版本:

import functools,mathdef required(*argss):    def required_01(fun):        @functools.wraps(fun)        def wrapper(*args):            for i in args:                if not isinstance(i,argss):    #或者用isinstance(i,int)                    print(‘參數必須是:‘,*argss)                    return None                    break            else:                res=fun(*args)                return res        return wrapper    return required_01@required(str,int)  #這裡可以看到要求傳入參數時str或者intdef add_num(*args):    return argsprint(add_num(‘ssss‘,789))    #符合要求print(add_num(‘aaaa‘,693,3.14))     #有浮點型,不符合要求

運行:

Python學習—裝飾器

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.