執行outer函數,將index做爲參數傳遞,python
將outer函數的返回值,從新賦值給index編程
裝飾器能夠在函數執行前和執行後執行其餘的附加功能,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator),裝飾器的功能很是強大,可是理解起來有些困難,所以我儘可能用最簡單的例子一步步的說明這個原理。app
寫代碼要遵循開發封閉原則,雖然在這個原則是用的面向對象開發,可是也適用於函數式編程,簡單來講,它規定已經實現的功能代碼不容許被修改,但能夠被擴展,即:函數式編程
假設我定義了一個函數f,想要在不改變原來函數定義的狀況下,在函數運行前打印出start,函數運行後打印出end,要實現這樣一個功能該怎麼實現?看下面如何用一個簡單的裝飾器來實現:函數
# 使用@語法放在函數的定義上面 至關於執行 f=outer(f),此時f賦值成爲了一個新的outer函數, # 此時f函數就指向了outer函數的返回值inner,inner是一個函數名,定義在oute函數裏面 # 原來的f是函數名可簡單理解爲一個變量,做爲outer函數的參數傳遞進去了 此時參數func至關於f def outer(func): # 定義一個outer函數做爲裝飾器 def inner(): # 若是執行inner()函數的話步驟以下: print('start') # 一、首先打印了字符‘start’, r=func() # 二、執行func函數,func函數至關於def f(): print('中') print('end') # 三、接着函數打印‘end’ return r # 四、將func函數的結果返回 return inner @outer def f(): # f=outer(f)=innner print('中') f() # f()至關於inner(),執行inner函數的步驟看上面定義處的註釋<BR>#打印結果順序爲 start 中 end
在實際中,咱們的裝飾器可能應用到不一樣的函數中去,這些函數的參數都不同,那麼咱們怎麼實現一個對任意參數都能實現功能的裝飾器?還記得我寫函數那篇博客中,就寫一種能夠接受任意參數的函數,下面來看看如何將其應用到裝飾器中去。spa
#其實只要將上面一種不帶參數的裝飾器修改一下就能夠了 #修改也很簡單,只需將inner和func的參數改成 (*args,**kwargs) #其餘實現的過程和上面一種同樣,就再也不介紹了 def outer(func): def inner(*args,**kwargs): print('start') r=func(*args,**kwargs) # 這裏func(*args,**kwargs)至關於f(a,b) print('end') return r return inner @outer def index(a,b): print(a+b) m=index(1,4) # f(1,4)至關於inner(1,4) 這裏打印的結果爲 start 5 end
print m
一、outer函數加載
二、遇到outer裝飾器
三、執行outer函數,將index函數(被裝飾的函數)做爲參數傳遞func=index()
四、加載inner函數,
五、將outer函數的返回值inner(這是一個函數),從新賦值給index(也就是說此時的index指向inner函數)
六、執行inner函數
七、打印‘start’
八、執行res = func(a1, a2)(也就是執行被裝飾器裝飾的函數(index函數))
九、將index函數的返回值賦值給res
十、打印‘end’
十一、將inner函數的返回值 res 賦值給index函數
十二、打印m
當一個裝飾器不夠用的話,咱們就能夠用兩個裝飾器,固然理解起來也就更復雜了,當使用兩個裝飾器的話,首先將函數與內層裝飾器結合而後在與外層裝飾器相結合,要理解使用@語法的時候到底執行了什麼,是理解裝飾器的關鍵。這裏仍是用最簡單的例子來進行說明。3d
def outer2(func2): def inner2(*args,**kwargs): print('開始') r=func2(*args,**kwargs) print('結束') return r return inner2 def outer1(func1): def inner1(*args,**kwargs): print('start') r=func1(*args,**kwargs) print('end') return r return inner1 @outer2 # 這裏至關於執行了 f=outer1(f) f=outer2(f),步驟以下 @outer1 #一、f=outer1(f) f被從新賦值爲outer1(1)的返回值inner1, def f(): # 此時func1爲 f():print('f 函數') print('f 函數') #二、f=outer2(f) 相似f=outer2(inner1) f被從新賦值爲outer2的返回值inner2 # 此時func2 爲inner1函數 inner1裏面func1函數爲原來的 f():print('f 函數') f() # 至關於執行 outer2(inner1)() >>開始 # 在outer函數裏面執行,首先打印 ‘開始 ’ >>start # 執行func2 即執行inner1函數 打印 ‘start’ >>f 函數 # 在inner1函數裏面執行 func1 即f()函數,打印 ‘f 函數’ >>end # f函數執行完,接着執行inner1函數裏面的 print('end') >>結束 # 最後執行inner2函數裏面的 print('結束')
前面的裝飾器自己沒有帶參數,若是要寫一個帶參數的裝飾器怎麼辦,那麼咱們就須要寫一個三層的裝飾器,並且前面寫的裝飾器都不太規範,下面來寫一個比較規範帶參數的裝飾器,下面來看一下代碼,你們能夠將下面的代碼自我運行一下orm
import functools def log(k=''): #這裏參數定義的是一個默認參數,若是沒有傳入參數,默認爲空,能夠換成其餘類型的參數 def decorator(func): @functools.wraps(func) #這一句的功能是使被裝飾器裝飾的函數的函數名不被改變, def wrapper(*args, **kwargs): print('start') print('{}:{}'.format(k, func.__name__)) #這裏使用了裝飾器的參數k r = func(*args, **kwargs) print('end') return r return wrapper return decorator @log() # fun1=log()(fun1) 裝飾器沒有使用參數 def fun1(a): print(a + 10) fun1(10) # print(fun1.__name__) # 上面裝飾器若是沒有@functools.wraps(func)一句的話,這裏打印出的函數名爲wrapper @log('excute') # fun2=log('excute')(fun2) 裝飾器使用給定參數 def fun2(a): print(a + 20) fun2(10)
一個函數能夠被多個裝飾器裝飾嗎? 好比兩個裝飾器 對象
會先將 函數交個最下層@裝飾器將處理結果在交給其上一層@裝飾器 即先交給w2 再交給w1blog
def w1(func): def inner(*args,**kwargs): # 驗證1 # 驗證2 # 驗證3 return func(*args,**kwargs) return inner def w2(func): def inner(*args,**kwargs): # 驗證1 # 驗證2 # 驗證3 return func(*args,**kwargs) return inner @w1 @w2 def f1(arg1,arg2,arg3): print 'f1'
雙層裝飾器原理