Python基礎(裝飾器)

代碼的編寫和軟件的開發,都應該遵循開放封閉原則。

開放封閉原則(OCP,Open Closed Principle)是所有面向對象原則的核心。其核心思想是:

對擴展開放,意味着有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。

對修改封閉,意味着類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。

而裝飾器(就是函數)的作用就是爲已經存在的對象添加額外的功能。


此篇文章主要介紹裝飾器的五種使用情況。

需求場景:

讓一個已經開發完成的功能在執行之前,先執行某些操作。


一、基本用法

原函數:

def func1():

    print 'old func1'

func1()

執行結果:

old func1


定義裝飾器:

def w1(func):    #這裏參數func的值就是被裝飾的函數的函數名

    def _w1():   #定義一個新的函數

        print 'Begin new func %s()'%func.func_name  #裝飾器中新加入的操作

        func()  #執行完新加入操作後開始執行原函數

    return _w1   #返回被重新裝飾過的函數,函數名爲_w1 


使用裝飾器裝飾原函數:

@w1        #裝飾器用法,@+裝飾器函數名,放在需要被裝飾的函數上面

def func1():   

    print 'old func1'

func1()

執行結果:

Begin new func func1()

old func1


二、動態參數,可以裝飾帶有N個(N>=0)參數的函數

def w2(func):

    def _w2(*args,**kwargs):    #可接受任意參數的函數

        print 'Begin new func %s()'%func.func_name

        func(*args,**kwargs)

    return _w2 


@w2

def func1():        #不帶參數的函數

    print 'old func1'

@w2

def func2(*args,**kwargs): #帶任意參數的參數

    print 'old func2,arg is %s'%args


func1()    #執行不帶參數的函數

func2('test') #執行帶參數的函數,並賦值一個test參數

執行結果:

Begin new func func1()

old func1

Begin new func func2()

old func2,arg is test


三、被裝飾的函數帶有return值

錯誤寫法:

def w3(func):

    def _w3(*args,**kwargs):

        print 'Begin new func %s()'%func.func_name

        func(*args,**kwargs)

    return _w3

@w3

def func3(*args,**kwargs):

    print 'old func3,arg is %s'%args

    return 'done'    #定義函數的返回值爲done


re = func3('test')    #獲取函數的返回值

print re        #打印函數的返回值

執行結果:

Begin new func func3()

old func3,arg is test

None    #結果函數的返回值爲None


正確寫法:

def w3(func):

    def _w3(*args,**kwargs):

        print 'Begin new func %s()'%func.func_name

        return func(*args,**kwargs)

    return _w3

@w3

def func3(*args,**kwargs):

    print 'old func3,arg is %s'%args

    return 'done'


re = func3('test')

print re

執行結果:

Begin new func func3()

old func3,arg is test

done    #得到正確的返回值,請自行查看兩種寫法的差別。


四、多個裝飾器裝飾同一個函數(多個裝飾器的情況下,從裏往外(下往上)裝飾,裝飾完成後,從外往裏(上往下)執行)

應用場景:同一個函數希望擴展兩個完全不一樣的功能,比如一個驗證功能,一個記錄日誌的功能。


用一個例子進行說明:

def first(func):    #裝飾器1

    print '%s() was post to first()'%func.func_name

    def _first(*args,**kw):

        print 'Call the function %s() in _first().'%func.func_name

        return func(*args,**kw)

    return _first


def second(func):    #裝飾器2

    print '%s() was post to second()'%func.func_name

    def _second(*args,**kw):

        print 'Call the function %s() in _second().'%func.func_name

        return func(*args,**kw)

    return _second


@first     #裝飾器1

@second    #裝飾器2

def test(*args,**kwargs):

    print 'test() args -->',args

test('123')

執行結果:

test() was post to second()

_second() was post to first()

Call the function _second() in _first().

Call the function test() in _second().

test() args --> ('123',)


逐條解釋:

test() was post to second()     #由於裝飾器second在test函數上面,所以會先用裝飾器second裝飾test,這時候裝飾器second的參數是test,執行後的返回值是_second函數,裏面的func變量值是test函數

_second() was post to first()   #由於_second函數上還有一個裝飾器first,所以會用裝飾器first裝飾_second,這時候裝飾器first的參數是_second, 執行後的返回值是_first函數,裏面的func變量值是_second函數

Call the function _second() in _first().    #所有裝飾器裝飾完成,開始執行,這裏會先執行_first函數,等於在裏面執行_second()

Call the function test() in _second().  #開始執行_second函數,等於在裏面執行test()

test() args --> ('123',)   #test函數執行的結果


五、多層裝飾器,(帶參數的裝飾器、不要和第二種情況搞混)

應用場景:當希望使用裝飾器裝飾函數時,裝飾器內部調用的方法是可定義的,比如裝飾A函數時,內部調用AA方法,裝飾B函數時,內部調用BB方法。


用一個例子進行說明:

def Before(request,kargs):

    print 'before -->',request


def After(request,kargs):

    print 'after <--',kargs


def Filter(before_func,after_func):    #第一層裝飾器

    def outer(main_func):    #第二層裝飾器

        def wrapper(request,kargs):  #第二層裝飾器定義的函數  


            before_result = before_func(request,kargs)

            if(before_result != None):

                return before_result;


            main_result = main_func(request,kargs)

            if(main_result != None):

                return main_result;


            after_result = after_func(request,kargs)

            if(after_result != None):

                return after_result;


        return wrapper    #第二層裝飾器返回的函數

    return outer    #第一層裝飾器返回的函數,也就是另一個裝飾器


@Filter(Before, After)

def Index(request,kargs):

    print 'index'

Index('begin','end')

執行結果:

before --> begin

index

after <-- end

執行過程:

1、解釋器讀取到裝飾器@Filter(Before, After),開始執行Filter(Before, After)函數

2、進入Filter函數執行,並傳入參數'Before, After',此時Filter函數中參數before_func=Before,參數after_func=After。

 繼續往下執行,函數中定義了一個outer函數,由於未調用該函數,所以不執行該函數,繼續往下執行

3、Filter函數return了outer函數作爲本次執行的返回值

4、根據Filter函數執行的返回值創建了新的裝飾器@outer

5、開始執行outer函數,此時其參數main_func的值爲原函數Index,也就是main_func=Index

6、outer函數中定義了另一個函數wrapper,其參數的值是原函數Index傳入的參數值,由於未調用該函數,所以不執行該函數,繼續往下執行

7、outer函數return了wrapper函數作爲本次執行的返回值

8、原函數Index被重新賦值等於wrapper函數,也就是Index=wrapper

9、開始執行新的Index函數(即wrapper函數)也就是wrapper('begin','end')

10、執行時會先執行before_func(request,kargs),也就是Before('begin','end')

11、再執行main_func(request,kargs),也就是Index('begin','end')

12、最後執行after_func(request,kargs),也就是After('begin','end')

執行完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章