Python裝飾器是什么及怎么實現(xiàn)

本篇內(nèi)容介紹了“Python裝飾器是什么及怎么實現(xiàn)”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)雞冠,10余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220

裝飾器介紹

為何要用裝飾器

軟件的設(shè)計應(yīng)該遵循開放封閉原則,即對擴展是開放的,而對修改是封閉的。對擴展開放,意味著有新的需求或變化時,可以對現(xiàn)有代碼進行擴展,以適應(yīng)新的情況。對修改封閉,意味著對象一旦設(shè)計完成,就可以獨立完成其工作,而不要對其進行修改。

軟件包含的所有功能的源代碼以及調(diào)用方式,都應(yīng)該避免修改,否則一旦改錯,則極有可能產(chǎn)生連鎖反應(yīng),最終導(dǎo)致程序崩潰,而對于上線后的軟件,新需求或者變化又層出不窮,我們必須為程序提供擴展的可能性,這就用到了裝飾器。

什么是裝飾器

’裝飾’代指為被裝飾對象添加新的功能,’器’代指器具/工具,裝飾器與被裝飾的對象均可以是任意可調(diào)用對象。概括地講,裝飾器的作用就是在不修改被裝飾對象源代碼和調(diào)用方式的前提下為被裝飾對象添加額外的功能。裝飾器經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務(wù)處理、緩存、權(quán)限校驗等應(yīng)用場景,裝飾器是解決這類問題的絕佳設(shè)計,有了裝飾器,就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。

提示:可調(diào)用對象有函數(shù),方法或者類

 裝飾器的實現(xiàn)

函數(shù)裝飾器分為:無參裝飾器和有參裝飾兩種,二者的實現(xiàn)原理一樣,都是’函數(shù)嵌套+閉包+函數(shù)對象’的組合使用的產(chǎn)物。

無參裝飾器的實現(xiàn)
import time

def index():
    time.sleep(3)
    print('Welcome to the index page’)
    return 200

index() #函數(shù)執(zhí)行

遵循不修改被裝飾對象源代碼的原則,我們想到的解決方法可能是這樣

start_time=time.time()
index() #函數(shù)執(zhí)行
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))

考慮到還有可能要統(tǒng)計其他函數(shù)的執(zhí)行時間,于是我們將其做成一個單獨的工具,函數(shù)體需要外部傳入被裝飾的函數(shù)從而進行調(diào)用,我們可以使用參數(shù)的形式傳入

def le(func): # 通過參數(shù)接收外部的值
    start_time=time.time()
    res=le()
    stop_time=time.time()
    print('run time is %s' %(stop_time-start_time))
    return res

但之后函數(shù)的調(diào)用方式都需要統(tǒng)一改成

le(index)
le(其他函數(shù))

這便違反了不能修改被裝飾對象調(diào)用方式的原則,于是我們換一種為函數(shù)體傳值的方式,即將值包給函數(shù),如下

def timer(func):
    def le(): # 引用外部作用域的變量func
        start_time=time.time()
        res=func()
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return le

這樣就可以在不修改被裝飾函數(shù)源代碼和調(diào)用方式的前提下為其加上統(tǒng)計時間的功能,只不過需要事先執(zhí)行一次timer將被裝飾的函數(shù)傳入,返回一個閉包函數(shù)le重新賦值給變量名 /函數(shù)名index,如下

index=timer(index)
index()

至此我們便實現(xiàn)了一個無參裝飾器timer,可以在不修改被裝飾對象index源代碼和調(diào)用方式的前提下為其加上新功能。但我們忽略了若被裝飾的函數(shù)是一個有參函數(shù),便會拋出異常

def home(name):
    time.sleep(5)
    print('Welcome to the home page',name)

home=timer(home)
home('le')
#拋出異常
TypeError: wrapper() takes 0 positional arguments but 1 was given

之所以會拋出異常,是因為home(‘le’)調(diào)用的其實是ler(‘le’),而函數(shù)wrapper沒有參數(shù)。wrapper函數(shù)接收的參數(shù)其實是給最原始的func用的,為了能滿足被裝飾函數(shù)參數(shù)的所有情況,便用上args+*kwargs組合(見4.3小節(jié)),于是修正裝飾器timer如下

ef timer(func):
    def le(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return le

此時我們就可以用timer來裝飾帶參數(shù)或不帶參數(shù)的函數(shù)了,但是為了簡潔而優(yōu)雅地使用裝飾器,Python提供了專門的裝飾器語法來取代index=timer(index)的形式,需要在被裝飾對象的正上方單獨一行添加@timer,當(dāng)解釋器解釋到@timer時就會調(diào)用timer函數(shù),且把它正下方的函數(shù)名當(dāng)做實參傳入,然后將返回的結(jié)果重新賦值給原函數(shù)名

@timer # index=timer(index)
def index():
    time.sleep(3)
    print('Welcome to the index page')
    return 200
@timer # index=timer(home)?          def home(name):
    time.sleep(5)
    print('Welcome to the home page’,name)

如果我們有多個裝飾器,可以疊加多個

@deco3
@deco2
@deco1
def index():
    pass

疊加多個裝飾器也無特殊之處,上述代碼語義如下:

index=deco3(deco2(deco1(index)))
有參裝飾器的實現(xiàn)

了解無參裝飾器的實現(xiàn)原理后,我們可以再實現(xiàn)一個用來為被裝飾對象添加認(rèn)證功能的裝飾器,實現(xiàn)的基本形式如下

def deco(func):
    def wc(*args,**kwargs):
        編寫基于文件的認(rèn)證,認(rèn)證通過則執(zhí)行res=func(*args,**kwargs),并返回res
    return wc

如果我們想提供多種不同的認(rèn)證方式以供選擇,單從wc函數(shù)的實現(xiàn)角度改寫如下

def deco(func):
        def wc(*args,**kwargs):
            if driver == 'file':
                編寫基于文件的認(rèn)證,認(rèn)證通過則執(zhí)行res=func(*args,**kwargs),并返回res
            elif driver == 'MySQL':
                編寫基于mysql認(rèn)證,認(rèn)證通過則執(zhí)行res=func(*args,**kwargs),并返回res
        return wc

函數(shù)wc需要一個driver參數(shù),而函數(shù)deco與wc的參數(shù)都有其特定的功能,不能用來接受其他類別的參數(shù),可以在deco的外部再包一層函數(shù)auth,用來專門接受額外的參數(shù),這樣便保證了在auth函數(shù)內(nèi)無論多少層都可以引用到

def auth(driver):
    def deco(func):
        ……
    return deco

此時我們就實現(xiàn)了一個有參裝飾器,使用方式如下

先調(diào)用auth_type(driver='file'),得到@deco,deco是一個閉包函數(shù),
包含了對外部作用域名字driver的引用,@deco的語法意義與無參裝飾器一樣
@auth(driver='file') 
def index():     
    pass
@auth(driver='mysql') 
def home():
    pass

可以使用help(函數(shù)名)來查看函數(shù)的文檔注釋,本質(zhì)就是查看函數(shù)的doc屬性,但對于被裝飾之后的函數(shù),查看文檔注釋

@timer
def home(name):
    '''
    home page function
    :param name: str
    :return: None
    '''
    time.sleep(5)
    print('Welcome to the home page',name)

print(help(home))
'''
打印結(jié)果:

Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

None

在被裝飾之后home=wc,查看home.name也可以發(fā)現(xiàn)home的函數(shù)名確實是wc,想要保留原函數(shù)的文檔和函數(shù)名屬性,需要修正裝飾器

def timer(func):
    def wc(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    wrapper.__doc__=func.__doc__
    wrapper.__name__=func.__name__
    return wc

“Python裝飾器是什么及怎么實現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

網(wǎng)站標(biāo)題:Python裝飾器是什么及怎么實現(xiàn)
當(dāng)前網(wǎng)址:http://muchs.cn/article48/gceehp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司、App設(shè)計、商城網(wǎng)站建站公司、標(biāo)簽優(yōu)化網(wǎng)站設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)