干貨技術(shù),第一時(shí)間送達(dá)! 一、開頭說兩句大家好,我叫黎潘,很高興有機(jī)會(huì)給大家分享一些我的學(xué)習(xí)感受,作為一名零基礎(chǔ)轉(zhuǎn)行剛一年的測試新手來說,深知自己在技術(shù)經(jīng)驗(yàn)方面落后太多,難免會(huì)有急于求成的心態(tài),這也就導(dǎo)致自己在學(xué)習(xí)新知識(shí)時(shí)似懂非懂,剛開始學(xué)完那會(huì)還胸有成竹,一段時(shí)間之后卻又忘的一干二凈,導(dǎo)致我要不?;厝?fù)習(xí),還始終不得要領(lǐng),難以在實(shí)踐中靈活運(yùn)用。 相信有不少同學(xué)跟我一樣徘徊躊躇,現(xiàn)在老師給予了我一個(gè)給大家分享經(jīng)驗(yàn)的機(jī)會(huì),我也剛好結(jié)合前段時(shí)間復(fù)習(xí)關(guān)于Python裝飾器的理解來說下,若有不對(duì)的地方,還望各位同學(xué),同行,老師及時(shí)指出。 二、裝飾器必知基礎(chǔ)其實(shí)很多知識(shí)點(diǎn)沒有牢牢掌握,是因?yàn)樽钭罨A(chǔ)的知識(shí)沒有理解透徹導(dǎo)致。這也是我在學(xué)習(xí)裝飾器時(shí)對(duì)于自己的評(píng)價(jià),所以先讓我們來聊聊學(xué)習(xí)裝飾器所需要的基礎(chǔ)知識(shí)。 1、形參與實(shí)參函數(shù)的參數(shù)分為形式參數(shù)和實(shí)際參數(shù),簡稱形參和實(shí)參。
具體使用時(shí)又分為位置參數(shù),關(guān)鍵字參數(shù)和默認(rèn)參數(shù)
上述示例中,調(diào)用函數(shù)時(shí)以key=value形式的就是關(guān)鍵字參數(shù),定義函數(shù)時(shí)name,age為位置參數(shù),sex為默認(rèn)參數(shù)。 注意:
2、可變長度參數(shù)*args和**kwargs參數(shù)的長度可變指的是調(diào)用函數(shù)時(shí),實(shí)參的個(gè)數(shù)可以不固定,而在調(diào)用階段,實(shí)參無非是按照位置或者按關(guān)鍵字兩種形式,因此就出現(xiàn)了兩種解決方案來處理。 2.1 可變長度的位置參數(shù)如果在最后一個(gè)形參名前加*號(hào),那么在調(diào)用函數(shù)時(shí),溢出的位置實(shí)參都會(huì)被接受,以元組的形式保存下來賦值給該形參。
2.2 可變長度的關(guān)鍵字參數(shù)如果在最后一個(gè)形參名前加**號(hào),那么在調(diào)用函數(shù)時(shí),溢出的關(guān)鍵字參數(shù),會(huì)以字典的形式保存下來賦值給形參。
2.3 組合使用可變參數(shù)*args與關(guān)鍵字參數(shù)kwargs通常是組合在一起使用的,如果一個(gè)函數(shù)的形參為上述兩種類型,那么代表該函數(shù)可以接收任何形式,任意長度的參數(shù)。
在該函數(shù)內(nèi)部還可以把接受到的實(shí)參傳給另一個(gè)函數(shù),這在后面推導(dǎo)裝飾器時(shí)大有用處。
分析: 此處在給wrapper傳參時(shí),其遵循的事函數(shù)func的參數(shù)規(guī)則,第一步,位置參數(shù)1被接受,以元組形式保存下來賦值給args,即args=(1,),關(guān)鍵字參數(shù)y=2,z=3被**以字典形式接收賦值給kwargs,即kwargs={'y':2,'z':3};第二步,執(zhí)行func(args,kwargs),即func((1,),{'y':2,'z':3}),等同于func(1,y=2,z=3)。 3、函數(shù)對(duì)象和閉包函數(shù)對(duì)象指的是函數(shù)可以被當(dāng)做"數(shù)據(jù)"來處理,具體可以分為四個(gè)方面的使用 3.1 函數(shù)可以被引用
3.2 函數(shù)可以作為容器類型的元素
3.3 函數(shù)可以作為參數(shù)傳入另一個(gè)函數(shù)
3.4 函數(shù)的返回值可以是一個(gè)函數(shù)
3.5 閉包函數(shù)有兩個(gè)關(guān)鍵點(diǎn)
總結(jié): 閉包函數(shù)提供了一種新的為函數(shù)體傳參的方式,為了給f2傳值,在他的同級(jí)作用域給了他一個(gè)值,f2在整體縮進(jìn),外層再給他嵌套一個(gè)函數(shù)f1包起來。此時(shí)f1從原來的全局變成了局部,為了使我們?cè)谌忠廊豢梢哉{(diào)用它,通過return函數(shù)對(duì)象再返回到全局。 三、什么是裝飾器上邊講了這么多,可能大家有點(diǎn)疑惑怎么還不介紹裝飾器。不用急,這也是我們?cè)趯W(xué)習(xí)中常犯的錯(cuò)誤,急于求成反而不利于對(duì)知識(shí)的吸收好消化理解。其實(shí)在潛移默化中,我們已經(jīng)把大部分構(gòu)成裝飾器的基本知識(shí)提到了,只是還未進(jìn)行歸納整理。下面我們又將重新一步一步推導(dǎo)它的由來。 定義:定義一個(gè)函數(shù)(類),該函數(shù)專門用來為其他函數(shù)(對(duì)象)添加額外的功能。 裝飾器本質(zhì)上是一個(gè)python函數(shù)或類,它可以讓其他函數(shù)或類在不需要做任何代碼修改的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)/類對(duì)象。它經(jīng)常用于有切面需求的場景,比如:插入日志,性能測試,事務(wù)處理,緩存,權(quán)限校驗(yàn)等場景。 四、為什么用裝飾器我們?cè)跒橐粋€(gè)對(duì)象添加新功能時(shí),往往秉持著開放封閉原則。
即在不修改被裝飾對(duì)象源代碼和調(diào)用方式的情況下為被裝飾對(duì)象新增功能。有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼到裝飾器中并繼續(xù)重用。 五、裝飾器的推導(dǎo)提出需求:為index函數(shù)新增計(jì)算代碼運(yùn)行時(shí)間的功能,必須符合開放封閉原則。
1、方案一
結(jié)果:雖然實(shí)現(xiàn)了功能,但破環(huán)了開放封閉原則,修改了源代碼,不符合要求,失敗。 2、方案二
結(jié)果:上述代碼雖然沒有修改源代碼,也實(shí)現(xiàn)了功能,但是每次使用都要加上這三行代碼,太過冗余,失敗。 3、方案三
結(jié)果:此時(shí)我們不用每次加上三行代碼,只需調(diào)用wrapper函數(shù)即可,但是復(fù)用性依然不夠,可以在進(jìn)行優(yōu)化。 4、方案四優(yōu)化:解決index的傳參被寫死了的問題
結(jié)果:進(jìn)行了一系列的優(yōu)化,我們發(fā)現(xiàn)雖然傳參的問題解決了,但是這個(gè)時(shí)候index函數(shù)也寫死了,以后的需求中不可能只有它需要這個(gè)功能,復(fù)用性不夠,因此可以繼續(xù)優(yōu)化。 5、方案五優(yōu)化:index寫死了的問題
6、方案五優(yōu)化:上面看是已經(jīng)優(yōu)化得差不多了,其實(shí)還是有漏洞,原函數(shù)index是沒有返回值的,此時(shí)調(diào)用換掉之后的index之后,返回的時(shí)wrapper的內(nèi)存地址,它并沒有返回值,index()返回的是None,沒有做到天衣無縫。這個(gè)時(shí)候就要用到我們上面講到的函數(shù)對(duì)象的引用可以作為返回值,問題就迎刃而解了。
6、最終方案(推薦)
這就是一個(gè)最簡單的無參裝飾器的模版,我們想要給某個(gè)對(duì)象也就是func函數(shù)添加新功能時(shí)直接在wrapper函數(shù)內(nèi)部書寫代碼即可。 關(guān)于有參裝飾器,此處由于篇幅限制就不在說明。有參也就說明我們的函數(shù)內(nèi)部需要一個(gè)參數(shù),無非就是兩種方式,一種通過形參直接傳入,另一種就是通過閉包函數(shù)直接包給它,此處肯定是利用閉包函數(shù)更合理。 六、感言誤打誤撞,因?yàn)槔蠋煹囊粋€(gè)課后作業(yè)任務(wù),完成了本人的第一篇知識(shí)總結(jié)。剛開始是有點(diǎn)驚慌的,但是隨之而來的是驚喜,雖然擔(dān)心寫的不夠好,但也算是想給自己一個(gè)交代,一個(gè)好的開始。知識(shí)的持續(xù)分享總結(jié)能夠促進(jìn)我們持續(xù)的學(xué)習(xí)進(jìn)步。 經(jīng)過這次小小的分享,回到開頭,我想說的就是學(xué)習(xí)一些高階知識(shí),當(dāng)我們感到模模糊糊的時(shí)候,不妨回歸本質(zhì),從最基礎(chǔ)的原理對(duì)他進(jìn)行分解,一步一步推導(dǎo),往往能給到我們一種醍醐灌頂,意想不到的收獲。最后希望同大家一道能通過這次的學(xué)習(xí),提升自己,完成自己的初心。 |
|