從新學習完了函數,是時候將其中的一些重點從新捋一捋了,本次總結的東西只有閉包和裝飾器python
1.閉包閉包
閉包是python函數中的一個比較重要功能,通常閉包都是用在裝飾器上,通常學完閉包就會去學習裝飾器,這倆個老是讓初學時的我一臉懵逼,如今好好捋一捋。函數
1.1 閉包的定義學習
1.2 閉包的判斷測試
__closure__的使用spa
####不是閉包 name = 'test' def func(): def inner(): print(name) return inner() f = func() print(f.__closure__[0].cell_contents) #結果 TypeError: 'NoneType' object is not subscriptable #結果就是沒有閉包調用的值,就不是閉包 #是閉包 def func(): name = 'test' age = 18 def inner(): print(name) print(age) return inner f = func() print(f.__closure__[0].cell_contents) print(f.__closure__[1].cell_contents) #結果 18 test
1.3 閉包的機制設計
在編譯器中碰到函數,在函數執行結束後會將名稱空間關閉,不過對於閉包,內部機制是碰到閉包的空間就不會關閉,在一段時候不在調用或者使用後纔會關閉日誌
1.4 閉包的使用以及使用場景code
用閉包的形式寫一個數字的連加blog
def func(i): num = 1 def inner(): nonlocal num num += i print(num) return inner f = func(3) for i in range(5): f()
上面這個只是一個很簡單的功能,對於閉包在一些平常的使用場景有
裝飾器和爬蟲的一部分使用
2.裝飾器(一個需求引起的血案)
需求:公司內一個項目運營了一段時間,TeamLeader以爲如今的代碼執行效率有點低下,須要對現有函數的的執行效率進行一次測試。
2.1 直接調用
在最開始的時候沒想完整,只是實現功能,代碼以下
import time def func1(): print('func1') time.sleep(0.5) def func2(): print('func2') time.sleep(0.6) def test(f): stat_time = time.time() f() end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) test(func1) test(func2) #結果 func1 執行效率爲0.5010201930999756 func2 執行效率爲0.6002087593078613
對於這個,你的領導絕對想要殺了你,這樣的話打亂了整個項目的函數調用順序,測試一遍,所有的函數基本無法用了,須要改進,改進目的是減小對於已有函數的影響
2.2 必定的假裝
import time def func1(): print('func1') time.sleep(0.5) def func2(): print('func2') time.sleep(0.6) def test(f): stat_time = time.time() f() end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) f = func1 func1 =test func1(f) f1 = func2 func2=test func2(f1) #結果和上面一致
和最開始的那個版本相比呢,有必定的假裝了,但是執行的方式和之前就不同了,還有一個比較重要的是這樣執行函數不能傳參數,項目的函數之間調用必定是有參數的,因此這個版本也PASS了,繼續想吧,下個階段實現的目的是執行函數和之前一致
2.3 比較好的假裝
def test(f): def inner(): stat_time = time.time() f() end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) return inner #結果 func1 執行效率爲0.5008118152618408
不太好理解,借用老師上課的一張圖,有空了在去作動圖吧
這個版本已經和很是接近完整版了,不過python中有語法糖的功能,能夠將上述函數儘可能減小
2.4 假裝成功-裝飾器
看一下代碼
import time def test(f): def inner(): stat_time = time.time() f() end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) return inner @test def func1(): print('func1') time.sleep(0.5) @test def func2(): print('func2') time.sleep(0.6) func1() func2()
看一下這個@test就是這個小老鼠,裝飾器加上下面的函數名,就自動生成一個裝飾器,這樣就看着很順眼了,不過還有一些功能啊,有些函數仍是有一些參數須要傳參,貌似如今的功能尚未實現哎,在改改吧。
2.5 裝飾器的傳參
既然傳參,那麼參數的數量就不一致,這樣就須要動態傳參了,看代碼
import time def test(f): def inner(*args,**kwargs): stat_time = time.time() f(*args,**kwargs) end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) return inner @test def func1(x): print(x) time.sleep(0.5) @test def func2(x,y): print(x,y) time.sleep(0.6) func1('a') func2('b',[1,2,3,4]) #結果 a 執行效率爲0.5004942417144775 b [1, 2, 3, 4] 執行效率爲0.6011292934417725
2.6 裝飾器的返回值
上面那個應該是已經很是很是接近於徹底體了,即便是函數,有了傳參,那麼就有返回值,那麼返回值怎麼才能看到呢,代碼以下
import time def test(f): def inner(*args,**kwargs): stat_time = time.time() r1 =f(*args,**kwargs) end_time = time.time() print('執行效率爲%s'%(end_time-stat_time)) return r1 return inner @test def func1(x): print(x) time.sleep(0.5) return 666 @test def func2(x,y): print(x,y) time.sleep(0.6) return 333 print(func1('a')) print(func2('b',[1,2,3,4])) #結果 a 執行效率爲0.5004842281341553 666 b [1, 2, 3, 4] 執行效率爲0.6007628440856934 333
寫到如今才終於把整個迭代器搞明白了,哎,之後的工做就是伴隨着裝飾器啊
2.7 裝飾器的總結
對上述全部的迭代器總結下就是下面這個例子了
def test(f): def inner(*args,**kwargs): #對函數進行裝飾以前的代碼 ret = f(*args,**kwargs)#返回值 #對函數進行裝飾以後的代碼 return ret#將函數的返回值返回出去 return inner #裝飾器的基本結構就是這個啦。。。
裝飾器:裝飾器的本質是閉包,並且裝飾器其實就是個函數而已。
裝飾器:在不改變原函數調用方式上,給原函數增長了一些額外的功能。登陸驗證,寫日誌,執行效率等等。
3.延伸:代碼開發的開放封閉原則
開放封閉原則:
對拓展開放的:
咱們說,任何一個程序,不可能在設計之初就已經想好了全部的功能而且將來不作任何更新和修改。
因此咱們必須容許代碼擴展、添加新功能。
對修改是封閉的:
就像咱們剛剛提到的,由於咱們寫的一個函數,頗有可能已經交付給其餘人使用了,
若是這個時候咱們對其進行了修改,頗有可能影響其餘已經在使用該函數的用戶。
裝飾器就是開放封閉原則完美的體現。