代碼的編寫和軟件的開發,都應該遵循開放封閉原則。
開放封閉原則(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')
執行完成。