1.裝飾器的做用python
在不修改被裝飾對象的源代碼以及調用方式的前提下爲被裝飾對象添加新功能函數
原則:ui
1.不修改被裝飾對象的源代碼
2.不修改被裝飾對象的調用方式
目標:3d
爲被裝飾對象添加新功能
2.裝飾器的定義和使用code
來看下面的代碼:對象
index函數的做用是程序在隨機睡眠1到5秒以後,打印一句話blog
如今想爲index函數添加一個新功能:統計index函數的運行時間,該怎麼作呢??教程
修改index函數以下:內存
運行程序,執行結果以下:源碼
welcome to index page
cost time: 2.000999927520752
能夠看到,爲index函數添加新功能確實實現了,可是卻違反了開放封閉原則。
在符合開放封閉原則的前提下,若是想爲index函數添加新功能,此時就要使用裝飾器了
修改代碼
運行程序,查看執行結果
welcome to index page
run time: 1.0
從程序執行結果能夠看出,index函數的運行時間已經被統計出來了
可是查看源碼能夠知道,index函數的源碼確實沒有被修改,可是index的調用方式被修改了
並且還有一個問題就是,timmer這個裝飾器只能被用來裝飾index這個函數,若是之後想統計別的函數的運行時間,又要從新定義別的裝飾器,這樣也太不靈活了。
修改上面的代碼
運行程序,查看程序執行結果
welcome to index page
run time: 4.0
能夠看到,index函數的源代碼沒有被修改,index函數的調用方式也沒有改變,可是依然爲index函數添加了統計時間的功能,這裏使用的就是裝飾器了。
來分析下上面代碼的執行流程:
這就是裝飾器裝飾index函數的執行流程
3.裝飾器的簡化使用
如今我又有另一個函數home,如今我也想統計home函數的運行時間,能夠把代碼修改以下
運行程序,執行結果以下
welcome to index pagerun time: 3.0
welcome to home pagerun time: 4.0
能夠看到,每次調用統計程序運行時間的裝飾器timmer,都要先把被調用的函數的函數名做爲參數傳給timmer裝飾器
而後再把timmer裝飾器的執行結果賦值給被調用的函數名自己,最後才能調用被裝飾的函數,太麻煩了有沒有??
其實python中的裝飾器能夠簡化成下面的格式
程序執行結果
welcome to index pagerun time: 2.0
welcome to home pagerun time: 4.0
能夠看出,使用 @加裝飾器名添加到被裝飾對象的上方
的方式也能夠爲一個函數添加裝飾器中定義的功能
4.多個裝飾器的定義與調用
在上面的例子裏,定義並調用了一個統計程序運行時間的裝飾器timmer,
若是如今想爲index函數添加一個用戶認證的功能,能夠定義一個名爲auth的裝飾器
運行程序
從程序執行結果能夠看出,用戶登陸密碼驗證的裝飾器auth已經定義並被成功調用了
若是想爲index函數添加用戶認證的功能,又想統計index函數執行時間的功能,在使用裝飾器的狀況下該怎麼調用呢
在上面的代碼裏,爲index函數添加了兩個裝飾器,如今有一個問題,就是這兩個裝飾器究竟哪一個先被調用,哪一個後被調用呢??
來分析一下,
若是timmer裝飾器先被調用,那麼程序就會先執行timmer裝飾器,而後再執行auth裝飾器,提示輸入用戶名和密碼,
這樣一來timmer裝飾器統計的時間就會包括輸入用戶名和密碼的時間,這個時間會遠遠大於index函數睡眠的2秒種;
若是auth裝飾器先被調用,timmer裝飾器後被調用,那麼timmer裝飾器統計的運行時間就應該只包括index函數的執行時間值應該在2秒多一點點的時間範圍內
運行程序,先輸入錯誤的用戶名和密碼以使用程序的執行時間加長
從程序的執行結果能夠知道,程序是先運行timmer裝飾器,而後才運行auth裝飾器,因此timmer統計的時間就包括了用戶認證的時間,因此timmer統計到的程序運行時間遠遠大於index睡眠的2秒鐘
因此這裏得出一個結論:
當一個函數同時被兩個裝飾器裝飾時,加上函數最上面的裝飾器先執行,加在下面的裝飾器先裝飾
把上面例子裏的timmer裝飾器和auth裝飾器位置互換一下
運行index函數,依然先輸入錯誤的用戶名和密碼,增長用戶認證的時間
能夠看到,此次timmer統計到的時間只包含index函數的運行時間,不包含用戶進行認證的時間
來分析一下上面例子中,index函數被timmer裝飾器和auth裝飾器裝飾的代碼裝飾流程
在上面得出結論,一個函數同時被兩個裝飾器時,加在下面的裝飾器先裝飾
1.timmer裝飾器裝飾原始的index,能夠寫成:index=timmer(index)2.在timmer裝飾器中,timmer裝飾器其實是返回inner的內存地址,因此在這裏,index=inner3.timmer裝飾器裝飾完成後,由auth裝飾器來裝飾,此時能夠寫成index=auth(index),4.這裏auth括號裏的index已經再也不是原始index函數,而是已經被timmer裝飾事後的index了,因此index=auth(timmer(index))5.又由於timmer裝飾的結果等於inner函數的內存地址,因此:index=auth(inner)
至此,兩個裝飾器的裝飾過程已經知道了,來看程序的執行過程
因此這裏用戶輸入用戶名和密碼的時間不會被timmer裝飾器統計在內
5.被裝飾函數參數的設置與定義
先來看一段代碼
如上所示,home函數添加了一個參數,而index函數並無參數
按照正常的函數的定義與調用方式,調用index函數和home函數的方式應該是下面這種形式
index()home("python")
而後咱們運行程序就會發現,程序拋出了異常
說個異常說明inner函數不須要位置參數,可是咱們給了一個位置參數
回到timmer裝飾器定義的部分,能夠看到,timmer裝飾器的內部函數確實沒有定義參數
這樣一來,timmer裝飾器只能用於裝飾沒有參數的函數了,
咱們能夠在timmer裝飾器定義的時候爲inner函數添加一個參數
可是這樣一來,timmer裝飾器裝飾index函數的時候又會拋出異常,由於index函數沒有參數
File "E:\python_learn\py_code\test.py", line 27, in <module>index()
TypeError: inner() missing 1 required positional argument: 'name'
在不知道被裝飾函數的參數個數的狀況下,即被裝飾函數的參數可變長,且形式不固定的時候,