1. 程式人生 > >簡單瞭解Python裝飾器實現原理

簡單瞭解Python裝飾器實現原理

        有過開發經驗得朋友隊裝飾模式這個詞應該不陌生,裝飾裝飾,顧名思義就是指對我們原來有得東西進行裝飾,比如我們買了新房,那麼我們對毛坯房的裝修,就是對我們房子進行拓展,讓它更加完善!同樣得對於程式碼也是如此,我們就是對我們原有的功能進行功能的拓展!

一、假設我們有下面的一個場景

1、假設你現在是某個公司的工程師,你當前負責得事情就是公司某個專案整個系統架構基本功能得開發,後續會有很多同事會直接呼叫你寫得功能函式。於是你就風風火火得一頓狂擼,終於有了下面程式碼:
def test1():
    print("====給員工加薪了!====")
	
def test2():
    print("====出新版本,殺一個程式設計師祭天!====")

很好,同事們調得很舒服,系統跑得很好,老闆賺錢。。。

2、某一天,老闆突然來找你,說有個員工莫名奇妙被加薪了,並不是老闆自己加的,老闆說,加薪這裡要驗證下,只有我才有許可權給員工加薪。又說最近程式設計師急劇減少,被殺得太多了,也要加許可權,只有老闆才能殺!3、作為一個出色得程式設計師,這點要求當然沒問題,於是你又一頓加班加點狂改,把老闆的需求都是實現了,於是又有了下面的程式碼:
def test1():
    if ROLE == "boss":
    	print("====給員工加薪了!====")
    else:
        print("====不好意思,您不是老闆====")
	
def test2():
    if ROLE == "boss":
        print("====出新版本,殺一個程式設計師祭天!====")
    else:
	print("====不好意思,您不是老闆====")

4、這一切看起來很正常,但是我們不妨假設下,如果有10000個類似得功能函式需要新增類似得驗證、又或者新增其它的功能呢?我們姑且假設你單身多年得手速非常快,但是這樣搞法也是很浪費時間的吧?更何況貿然得去改動原來得程式碼,風險也是不小的,所以我們換下面這種方式看看!

二、閉包得引入

1、之前我已經分享過閉包的學習筆記了,不瞭解的可以去看下,閉包得幾個條件如下:一個函式內定義了內函式,並且內涵是使用了外函式的區域性變數,最後外函式返回了內涵的引用。滿足了這三個條件我們就可以稱之為閉包。

2、那麼我們到底引入閉包做什麼呢?閉包又是如何解決我們上面的問題得呢?我們先看下下面這段程式碼:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法驗證通過====")
	    fun()
	else:
	    print("====不好意思,您不是老闆====")
    return inner
				
def test1():
    print("====給員工加薪了!====")
	
def test2():
    print("====出新版本,殺一個程式設計師祭天!====")
		
		
#呼叫iwapper,並傳入test1函式的引用,給員工加薪
addMoney = iwapper(test1)
addMoney ()

#呼叫iwapper,並傳入test2函式的引用,殺程式設計師
killDev = iwapper(test2)
killDev()

列印下結果:


此時,我們產生了兩個新函式,一個是addMoney(),另一個是killDev(),呼叫這兩個即實現了新增驗證得功能,保持了原來功能函式不用修改,並且大大減少了程式碼得冗餘,但是另一個問題又來了,程式碼得呼叫方式變了,我們需要告知其他所有人,修改他們得程式碼!很顯然這是一個更糟糕得做法!

3、路總是一步步走出來得,經歷了那麼多波折,真相也越來越接近了,聰明得你很快又拿出了下面得方案:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法驗證通過====")
	    fun()
	else:
	    print("====不好意思,您不是老闆====")
    return inner
				
def test1():
    print("====給員工加薪了!====")
	
def test2():
    print("====出新版本,殺一個程式設計師祭天!====")
			
#呼叫iwapper,並傳如test1函式的引用,給員工加薪
test1 = iwapper(test1)
test ()

#呼叫iwapper,並傳如test2函式的引用,殺程式設計師
test2 = iwapper(test2)
test2()

列印下結果:


        這裡我們可以看到我們並沒有對原有功能函式進行修改,並且提供給外部呼叫得方式依然是test1()、test2(),已經滿足了我們得條件了,那麼我們一步步的分析下:1>首先定義了一個test1()函式的時候,就相當於再記憶體中建立了一塊記憶體區域,我們姑且叫它A。同樣得我們在定義iwapper(fun)的時候,在其內部定義了inner()函式,我們姑且把它所指向的記憶體區域命名為B。2>緊接著,當我們呼叫iwapper(test1)的時候,我們把test1()的引用傳了進去,所以fun此時指向了test1()的引用,也就是指向了A3>接下來我們使用test1接收了iwapper(fun)得返回值,而iwapper(fun)返回得是inner()的引用,所以此時test1指向了B4>所以最後我們呼叫test1()函式得時候其實是呼叫了inner(),而inner()函式除了進行校驗功能之外還呼叫了fun()函式,從第二點我們可以知道,fun指向了A也就是最開始定義得test1(),這樣我們即進行了驗證功能,又對原來功能進行呼叫!

三、裝飾器

1、說了那麼多,我們來看看如果遇到上面的需求,如果使用Python裝飾器來實現的話會怎麼樣?看下面程式碼:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法驗證通過====")
	    fun()
	else:
	    print("====不好意思,您不是老闆====")
    return inner
				
@iwapper
def test1():
    print("====給員工加薪了!====")

@iwapper
def test2():
    print("====出新版本,殺一個程式設計師祭天!====")

那麼看下效果:


很明顯,效果實現了,這時候也許就又朋友要說道說道了,nmmp,Python解析器那麼簡單,你前面說那麼多廢話幹嘛,直接一開始就來這段不就行了?朋友莫激動,先把刀放下,其實如果我們單看最後面的程式碼,我們僅僅知道@iwapper是python裝飾器的實現方式而已,但是我們並不知道這句程式碼到底幹了什麼事,而我們前面說的就是python解析器的實現原理了!其中得思想獲取會對我們有所幫助也不一定呢!

總結:這裡僅僅是對Python中裝飾器得最簡單使用方式進行學習,更重要得是學習Python決絕類似問題的思想!其更多用法大家可以查閱相關得資料,比如說帶引數的裝飾器,多個裝飾器一起使用等等!