溫馨提示×

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

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

Python中裝飾器的使用方法

發(fā)布時(shí)間:2020-08-13 13:45:36 來源:億速云 閱讀:139 作者:小新 欄目:編程語言

小編給大家分享一下Python中裝飾器的使用方法,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

裝飾器的使用

來看下面的代碼:

    import time
    import random
    
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    index()

index函數(shù)的作用是程序在隨機(jī)睡眠1到5秒之后,打印一句話。現(xiàn)在想為index函數(shù)添加一個(gè)新功能:統(tǒng)計(jì)index函數(shù)的運(yùn)行時(shí)間,該怎么做呢??

修改index函數(shù)如下:

    import time
    import random
    
    def index():
        start_time=time.time()
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
        end_time=time.time()
        print("cost time: %s" %(end_time - start_time))
    
    index()

運(yùn)行程序,執(zhí)行結(jié)果如下:

welcome to index page
cost time: 2.000999927520752

可以看到,為index函數(shù)添加新功能確實(shí)實(shí)現(xiàn)了,但是卻違反了開放封閉原則。在符合開放封閉原則的前提下,如果想為index函數(shù)添加新功能,此時(shí)就要使用裝飾器了。

修改代碼

  import time
  import random
    
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    def timmer():
        def inner():
            start_time=time.time()
            index()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
    
    f=timmer()
    f()

運(yùn)行程序,查看執(zhí)行結(jié)果

welcome to index page
run time: 1.0

從程序執(zhí)行結(jié)果可以看出,index函數(shù)的運(yùn)行時(shí)間已經(jīng)被統(tǒng)計(jì)出來了,但是查看源碼可以知道,index函數(shù)的源碼確實(shí)沒有被修改,但是index的調(diào)用方式被修改了。而且還有一個(gè)問題就是,timmer這個(gè)裝飾器只能被用來裝飾index這個(gè)函數(shù),如果以后想統(tǒng)計(jì)別的函數(shù)的運(yùn)行時(shí)間,又要重新定義別的裝飾器,這樣也太不靈活了。

修改上面的代碼

    import time
    import random
    
    def timmer(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
        
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    index=timmer(index)
    index()

運(yùn)行程序,查看程序執(zhí)行結(jié)果

welcome to index page
run time: 4.0

可以看到,index函數(shù)的源代碼沒有被修改,index函數(shù)的調(diào)用方式也沒有改變,但是依然為index函數(shù)添加了統(tǒng)計(jì)時(shí)間的功能,這里使用的就是裝飾器了。

來分析下上面代碼的執(zhí)行流程:

    1.導(dǎo)入time和random模塊,定義index函數(shù)和timmer函數(shù)。

    2.把原始的index函數(shù)的內(nèi)存地址作為參數(shù)傳給timmer函數(shù)。

    3.timmer函數(shù)內(nèi)部嵌套定義一個(gè)函數(shù)inner,然后返回inner函數(shù)的內(nèi)存地址。

    4.timmer函數(shù)執(zhí)行完成,返回timmer函數(shù)的內(nèi)部函數(shù)inner的內(nèi)存地址,然后把inner的內(nèi)存地址賦值給index變量。

    5.index是inner函數(shù)的內(nèi)存地址,index變量加括號(hào)運(yùn)行,實(shí)際上就是在運(yùn)行inner函數(shù)。

    6.運(yùn)行inner函數(shù),定義程序開始時(shí)間。

    7.執(zhí)行timmer函數(shù)的變量func,在第2步知道,func這個(gè)變量就是index的內(nèi)存地址,所以這里實(shí)際上是執(zhí)行被裝飾過后的index函數(shù)。

    8.index函數(shù)執(zhí)行完成,定義程序的終止時(shí)間。

    9.統(tǒng)計(jì)并打印整個(gè)程序的執(zhí)行過程中所花費(fèi)的時(shí)間。

這就是裝飾器裝飾index函數(shù)的執(zhí)行流程。

裝飾器的簡(jiǎn)化使用

現(xiàn)在我又有另外一個(gè)函數(shù)home,現(xiàn)在我也想統(tǒng)計(jì)home函數(shù)的運(yùn)行時(shí)間,可以把代碼修改如下:

    import time
    import random
    
    def timmer(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
    
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    def home():
        time.sleep(random.randrange(1,5))
        print("welcome to home page")
        
    index=timmer(index)
    index()
    
    home=timmer(home)
    home()

運(yùn)行程序,執(zhí)行結(jié)果如下:

welcome to index page
run time: 3.0 
welcome to home page
run time: 4.0

可以看到,每次調(diào)用統(tǒng)計(jì)程序運(yùn)行時(shí)間的裝飾器timmer,都要先把被調(diào)用的函數(shù)的函數(shù)名作為參數(shù)傳給timmer裝飾器,然后再把timmer裝飾器的執(zhí)行結(jié)果賦值給被調(diào)用的函數(shù)名本身,最后才能調(diào)用被裝飾的函數(shù),太麻煩了有沒有??

其實(shí)python中的裝飾器可以簡(jiǎn)化成下面的格式:

    import time
    import random
    
    def timmer(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
    
    @timmer
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    @timmer
    def home():
        time.sleep(random.randrange(1,5))
        print("welcome to home page")
    
    index()
    home()

程序執(zhí)行結(jié)果:

welcome to index page
run time: 2.0 
welcome to home page
run time: 4.0

可以看出,使用@加裝飾器名添加到被裝飾對(duì)象的上方的方式也可以為一個(gè)函數(shù)添加裝飾器中定義的功能。

多個(gè)裝飾器的定義與調(diào)用

在上面的例子里,定義并調(diào)用了一個(gè)統(tǒng)計(jì)程序運(yùn)行時(shí)間的裝飾器timmer,如果現(xiàn)在想為index函數(shù)添加一個(gè)用戶認(rèn)證的功能,可以定義一個(gè)名為auth的裝飾器。

    import time
    import random
    
    def auth(func):
        def wrapper():
            while True:
                user=input("Input your username>>>:").strip()
                pwd=input("Input your password>>>:").strip()
                if user== "abcd" and pwd == "abcd1234":
                    print("login successful")
                    func()
                    break
                else:
                    print("login error")
        return wrapper
    
    @auth
    def index():
        time.sleep(random.randrange(1,5))
        print("welcome to index page")
    
    index()

運(yùn)行程序:

    Input your username>>>:abcd             # 先輸入錯(cuò)誤的用戶名和密碼
    Input your password>>>:1234
    login error                             # 提示用戶輸入錯(cuò)誤,登錄失敗
    Input your username>>>:abcd             # 讓用戶再次輸入用戶名和密碼
    Input your password>>>:abcd1234
    login successful                        # 登錄成功
    welcome to index page                   # 執(zhí)行index函數(shù)

從程序執(zhí)行結(jié)果可以看出,用戶登錄密碼驗(yàn)證的裝飾器auth已經(jīng)定義并被成功調(diào)用了。

如果想為index函數(shù)添加用戶認(rèn)證的功能,又想統(tǒng)計(jì)index函數(shù)執(zhí)行時(shí)間的功能,在使用裝飾器的情況下該怎么調(diào)用呢?

    import time
    import random
    
    def timmer(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
    
    def auth(func):
        def wrapper():
            while True:
                user=input("Input your username>>>:").strip()
                pwd=input("Input your password>>>:").strip()
                if user== "abcd" and pwd == "abcd1234":
                    print("login successful")
                    func()
                    break
                else:
                    print("login error")
        return wrapper
    
    @timmer
    @auth
    def index():
        time.sleep(2)
        print("welcome to index page")
    
    index()

在上面的代碼里,為index函數(shù)添加了兩個(gè)裝飾器,現(xiàn)在有一個(gè)問題,就是這兩個(gè)裝飾器究竟哪個(gè)先被調(diào)用,哪個(gè)后被調(diào)用呢??

來分析一下,如果timmer裝飾器先被調(diào)用,那么程序就會(huì)先執(zhí)行timmer裝飾器,然后再執(zhí)行auth裝飾器,提示輸入用戶名和密碼,這樣一來timmer裝飾器統(tǒng)計(jì)的時(shí)間就會(huì)包括輸入用戶名和密碼的時(shí)間,這個(gè)時(shí)間會(huì)遠(yuǎn)遠(yuǎn)大于index函數(shù)睡眠的2秒種;如果auth裝飾器先被調(diào)用,timmer裝飾器后被調(diào)用,那么timmer裝飾器統(tǒng)計(jì)的運(yùn)行時(shí)間就應(yīng)該只包括index函數(shù)的執(zhí)行時(shí)間值應(yīng)該在2秒多一點(diǎn)點(diǎn)的時(shí)間范圍內(nèi)。

運(yùn)行程序,先輸入錯(cuò)誤的用戶名和密碼以使用程序的執(zhí)行時(shí)間加長(zhǎng)。

Input your username>>>:abcd
Input your password>>>:abcd
login error
Input your username>>>:abcd
Input your password>>>:abcd1234
login successful
welcome to index page
run time: 12.759000062942505

從程序的執(zhí)行結(jié)果可以知道,程序是先運(yùn)行timmer裝飾器,然后才運(yùn)行auth裝飾器,所以timmer統(tǒng)計(jì)的時(shí)間就包括了用戶認(rèn)證的時(shí)間,所以timmer統(tǒng)計(jì)到的程序運(yùn)行時(shí)間遠(yuǎn)遠(yuǎn)大于index睡眠的2秒鐘。

所以這里得出一個(gè)結(jié)論:

當(dāng)一個(gè)函數(shù)同時(shí)被兩個(gè)裝飾器裝飾時(shí),加上函數(shù)最上面的裝飾器先執(zhí)行,加在下面的裝飾器先裝飾。

把上面例子里的timmer裝飾器和auth裝飾器位置互換一下。

    import time
    import random
    
    def timmer(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print("run time: %s " %(end_time-start_time))
        return inner
    
    def auth(func):
        def wrapper():
            while True:
                user=input("Input your username>>>:").strip()
                pwd=input("Input your password>>>:").strip()
                if user== "abcd" and pwd == "abcd1234":
                    print("login successful")
                    func()
                    break
                else:
                    print("login error")
        return wrapper
    
    @auth
    @timmer
    def index():
        time.sleep(2)
        print("welcome to index page")
    
    index()

運(yùn)行index函數(shù),依然先輸入錯(cuò)誤的用戶名和密碼,增加用戶認(rèn)證的時(shí)間。

Input your username>>>:abcd
Input your password>>>:abcd
login error
Input your username>>>:abcd
Input your password>>>:abcd1234
login successful
welcome to index page
run time: 2.0

可以看到,這次timmer統(tǒng)計(jì)到的時(shí)間只包含index函數(shù)的運(yùn)行時(shí)間,不包含用戶進(jìn)行認(rèn)證的時(shí)間。

來分析一下上面例子中,index函數(shù)被timmer裝飾器和auth裝飾器裝飾的代碼裝飾流程。

    @auth           # index=auth(timmer(index))
    @timmer         # index=timmer(index)
    def index():
        time.sleep(2)
        print("welcome to index page")

在上面得出結(jié)論,一個(gè)函數(shù)同時(shí)被兩個(gè)裝飾器時(shí),加在下面的裝飾器先裝飾:

1.timmer裝飾器裝飾原始的index,可以寫成:index=timmer(index)。

2.在timmer裝飾器中,timmer裝飾器實(shí)際上是返回inner的內(nèi)存地址,所以在這里,index=inner。

3.timmer裝飾器裝飾完成后,由auth裝飾器來裝飾,此時(shí)可以寫成index=auth(index)。

4.這里auth括號(hào)里的index已經(jīng)不再是原始index函數(shù),而是已經(jīng)被timmer裝飾過后的index了,所以index=auth(timmer(index))。

5.又因?yàn)閠immer裝飾的結(jié)果等于inner函數(shù)的內(nèi)存地址,所以:index=auth(inner)。

至此,兩個(gè)裝飾器的裝飾過程已經(jīng)知道了,來看程序的執(zhí)行過程:

6.程序先執(zhí)行auth裝飾器,進(jìn)入用戶認(rèn)證,請(qǐng)用戶輸入用戶名和密碼。

7.用戶輸入正確的用戶名和密碼后,開始執(zhí)行func函數(shù),也已經(jīng)上面分析的inner函數(shù)。

8.timmer裝飾器先定義程序的開始運(yùn)行時(shí)間,然后運(yùn)行func函數(shù),也就是原生的index函數(shù)。

9.index函數(shù)先睡眠2秒,然后執(zhí)行print語句,再定義程序的結(jié)束時(shí)間。

10.最后統(tǒng)計(jì)并打印程序的運(yùn)行時(shí)間,至此程序運(yùn)行完畢。

所以這里用戶輸入用戶名和密碼的時(shí)間不會(huì)被timmer裝飾器統(tǒng)計(jì)在內(nèi)。

以上是Python中裝飾器的使用方法的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

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

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

AI