python閉包與裝飾器
本文針對: 學不會,學過即忘,學完跑路 的相關python人群.
一.Python閉包
定義: 訪問了定義體以外的定義的非全域性變數.(剛學時看不懂這句話太TM正常了)
定義解析: 其實就是函式裡面再定義一個函式,裡層函式引用了外層函式的變數,這就是閉包.
1 def wrapper(): 2 whatever = 'abandon' 3 def inner(): 4 print(whatever) 5 print(inner.__closure__) # 閉包: (<cell at 0x000002584285E558: str object at 0x000002584445F230>,)最簡單閉包形成6 7 wrapper()
這裡需要說明的是閉包形成與否和是否返回記憶體函式並無直接關係,只是這樣我們就無法運用閉包了,所以正常情況下必須返回.
二.Python裝飾器
1.這玩意有什麼用
裝飾器,裝飾器,自然是裝飾東西用的.這裡的"東西"即是python中的一等公民--函式.
2.函式為什麼需要裝飾
這個說起來話就長了,這裡長話短說,總結成一句話: 中心思想: 簡單省事以及--別動老子程式碼.
3.裝飾器的作用
這裡引入一個軟體設計的原則:
開放封閉原則--對擴充套件程式碼的功能是開放的,但是對修改原始碼是封閉的.(即功能你可以加,但是程式碼您可別動...)
那麼python中怎麼去實現呢--裝飾器啊!
4.裝飾器的寫法和引用
a.裝飾器需要用到閉包;
b.裝飾器需要返回記憶體函式;
c.裝飾器使用"@"符號呼叫;
ex:
1 def zsq(func): # zsq: decorator的拼音 2 def inner(): 3 print('臥槽!') # 加一個熱血沸騰的語氣詞 4 func() 5 return inner 6 7 @zsq #給函式加個文明的語氣詞呼叫裝飾器 8 def haoyougei(): 9 print("你不要過來啊!") 10 11 @zsq # 呼叫裝飾器 12 def aduogeng(): 13 print("為什麼是你!") 14 15 @zsq # 呼叫裝飾器 16 def axiba(): 17 print("居然是你!") 18 19 axiba() 20 aduogeng() 21 haoyougei() 22
如上述程式碼,為了防止定義的函式被再次更改程式碼(以及簡單方便),使用@呼叫裝飾器是事半功倍的方法.
5.裝飾器的特性:
a.能把被裝飾的函式替換成其他函式;
所以上面程式碼可以理解為:
axiba = zsq(axiba)
axiba()
aduogeng = zsq(aduogeng)
aduogeng()
haoyougei = zsq(haoyougei)
haoyougei()
b.裝飾器函式在載入模組上立即執行,不需要函式呼叫.(這一點請自行測試)
三.Python裝飾器進階
看了上面的部分後如果瞭解python函式的你在寫程式碼時就可能發現下面的兩個問題:
1.被裝飾的函式有傳參怎麼辦?
2.定義的裝飾器想傳參怎麼辦?
這裡引用一下上面的例子:
①axiba = zsq(axiba)
② axiba()
①裡面的執行完成後,axiba為zsq返回的inner,呼叫axiba()時執行的為inner(),此時記憶體中func儲存為axiba,所以執行func()即為執行axiba();
若axiba有傳參,如axiba(*args),則func中引數應該為*args,即func(*args),而func的傳參需要從inner中傳入,所以寫為inner(*args).
所以由上得出, 被裝飾的函式有傳參時,應該設定在裡層函式中,如下:
1 def zsq(func): # zsq: decorator的拼音 2 def inner(*args, **kwargs): # (*args, **kwargs)表示接受所有傳參 3 print('臥槽!') # 加一個熱血沸騰的語氣詞 4 func(*args, **kwargs) 5 return inner 6 7 @zsq # 呼叫裝飾器 8 def haoyougei(name): 9 print("你不要過來啊!{}".format(name)) 10 11 @zsq # 呼叫裝飾器 12 def aduogeng(name): 13 print("為什麼是你!{}".format(name)) 14 15 @zsq # 呼叫裝飾器 16 def axiba(name): 17 print("居然是你!{}".format(name)) 18 19 20 # axiba = zsq(axiba) 21 axiba('張益達') 22 # aduogeng = zsq(aduogeng) 23 aduogeng('律政先鋒') 24 # haoyougei = zsq(haoyougei) 25 haoyougei('斯內克')被裝飾的函式擁有傳參
解決了一個問題,還剩一個問題,其實第二個問題算是功能性問題--為了實現一些功能而出現的,不然一個正常人不會想到給裝飾器傳什麼引數.
怎麼解決呢,其實實現起來非常簡單,但是原理有一點繞: 想想上面第一個問題的引數是如何加進去的--最裡層呼叫的函式(func)需要傳參,則給其所在層的函式(inner)加上了傳參.那麼當裝飾器函式(zsq)需要傳參時,我們給其包裝一層函式,並在包裝函式上設定傳參不就可以了.
所以,當定義的裝飾器需要傳參時,需要在外層再設定一層包裝函式,並將包裝函式設定好傳參,最後將裝飾器函式返回.
1 def decorator(flag=True): # 給裝飾器設定一個引數: 形成一個開關 2 def zsq(func): # zsq: decorator的拼音 3 def inner(*args, **kwargs): # (*args, **kwargs)表示接受所有傳參 4 print('臥槽!') # 加一個熱血沸騰的語氣詞 5 if flag: # 使用外部flag標籤實現邏輯功能 6 func(*args, **kwargs) 7 else: 8 print('溜了溜了~') 9 return inner 10 return zsq # 返回裝飾器,zsq才是真正的裝飾器,decorator是其包裝 11 12 @decorator() # 呼叫裝飾器decorator ***** 呼叫的是最外層的包裝 13 def haoyougei(name): 14 print("你不要過來啊!{}".format(name)) 15 16 @decorator(flag=False) # 呼叫裝飾器decorator ***** 呼叫的是最外層的包裝 17 def aduogeng(name): 18 print("為什麼是你!{}".format(name)) 19 20 @decorator() # 呼叫裝飾器decorator ***** 呼叫的是最外層的包裝 21 def axiba(name): 22 print("居然是你!{}".format(name)) 23 24 25 # axiba = zsq(axiba) 26 axiba('張益達') 27 # aduogeng = zsq(aduogeng) 28 aduogeng('律政先鋒') 29 # haoyougei = zsq(haoyougei) 30 haoyougei('斯內克')裝飾器的傳參實現
到這裡基本上算是將python裝飾器的基本概念和實現說明完畢,另提一嘴from functools import wraps,想多瞭解的話google一下吧.
自己寫4到5個裝飾器,基本上就初步掌握了.
The End!