Python練習題詳解之內(nèi)嵌函數(shù)和閉包(小白友好)
你好斗这!歡迎來到Python練習題詳解系列。為了讓小白(也就是我本人)更好的理解代碼,打好編程基礎经瓷,我決定仔細地解說一些練習題翠储。歡迎閱讀绘雁!奧利給!
首先援所,我們來看看知識點~
1. 一般的編程語言都有局部變量local variable 和全局變量 global variable 之分庐舟。函數(shù)內(nèi)部的叫局部變量,函數(shù)外部的叫全局變量住拭。在函數(shù)外邊是無法訪問到函數(shù)內(nèi)部的局部變量的挪略。
2. 如果希望在函數(shù)中修改全局變量(global variable)的值,應該用global關鍵字滔岳。如圖杠娱,如果沒有‘global’關鍵字,我們的print(count)就會輸出5谱煤,因為count會自動輸出是全局變量的值摊求。
如果加上global,才可以修改全局變量的值趴俘。修改后睹簇,count就是10了。代表在函數(shù)內(nèi)部修改全局變量寥闪。
3.??Python函數(shù)定義是可以嵌套的太惠,也就是允許在函數(shù)的內(nèi)部創(chuàng)建另外一個函數(shù),這種函數(shù)叫做內(nèi)嵌函數(shù)或者內(nèi)部函數(shù)疲憋。
如圖凿渊,函數(shù)fun1里面嵌套了fun2,我們在定義階段在fun1里面調(diào)用了fun2缚柳,后來輸入fun1()的時候埃脏,就同時調(diào)用了fun1 和 fun2
值得注意的是:內(nèi)部函數(shù)(fun2)整個作用域都在外部函數(shù)(fun1)之內(nèi)。
另外需要注意的地方:如果在fun1()外部試圖調(diào)用內(nèi)部函數(shù)fun2()秋忙,就會報錯:
4.?閉包是函數(shù)編程的一個重要的語法結構彩掐。什么是閉包呢?
Python中閉包從表現(xiàn)形式上定義為:
如果在一個內(nèi)部函數(shù)內(nèi)(funY就是這個內(nèi)部函數(shù))灰追,
對外部作用域(但不是在全局作用域)的變量進行引用
(這里x就是被引用的變量堵幽,x在外部作用域funX里面狗超,但不在全局作用域里),
那么內(nèi)部函數(shù)就被認為是閉包朴下。
也就像俄羅斯套娃努咐,小的娃娃(內(nèi)部函數(shù))對大的娃娃(外部作用域)的變量進行引用。
使用閉包時殴胧,需要注意的是:因為閉包的概念就是由內(nèi)部函數(shù)而來的渗稍,所以也不能在外部函數(shù)以外的地方對內(nèi)部函數(shù)進行調(diào)用:也就是說,離開了大娃娃团滥,小娃娃就沒有價值了竿屹。
在閉包中,外部函數(shù)的局部變量對應內(nèi)部函數(shù)的局部變量惫撰,
實際上就相當于之前講的全局變量跟局部變量的關系羔沙,
在內(nèi)部函數(shù)中,你只能對外部函數(shù)的局部變量進行訪問厨钻,但不能進行修改扼雏。舉個栗子:
在下面這個代碼中,我們在第四行夯膀,對x的值進行了修改诗充,讓x = x*x,所以就返回了一個錯誤消息诱建,說明在內(nèi)部函數(shù)中蝴蜓,是不能對外部函數(shù)的變量進行修改滴!Python認為在內(nèi)部函數(shù)的x是局部變量的時候俺猿,外部函數(shù)的x就被屏蔽了起來茎匠,所以執(zhí)行x *=?x的時候,在右邊根本找不到局部變量x的值押袍,因此報錯诵冒。
在這里我們有一種方法,就像前文提到的 ‘global’ 一樣谊惭,我們這里在x前面加一個‘nonlocal’汽馋,說他不是本地人,哈哈哈哈哈圈盔,這樣x就變成一個函數(shù)國際范兒豹芯,就可以被修改啦。
知識點over啦驱敲,讓我們來看看題目:
題目1
你覺得下面的程序運行出來會是什么呢铁蹈?
思路:
首先,我們看看這段代碼众眨,很明顯木缝,他是兩個函數(shù)的嵌套便锨。由外部函數(shù)funX和內(nèi)部函數(shù)funY組成围辙。內(nèi)部函數(shù)funY引用了外部函數(shù)funX的變量x我碟,所以funY是一個閉包。
而在閉包里面姚建,給這個x上了一個nonlocal矫俺,說明在內(nèi)部函數(shù)里也可以改變x的值。在這里掸冤,x會增加1厘托。retun x,說明運行funY之后會輸出x的值稿湿。
在第九行和第十行铅匹,funX() 分別等于 a 和 b,也就是說饺藤,a = funX() 包斑,b?= funX() 代表 a 和 b 調(diào)用了funX函數(shù),這時涕俗,x = 5罗丰。
然后在第十二行,在print() 里面再姑,輸入了 a() 萌抵,說明對? a() 進行了調(diào)用。而 a 本來已經(jīng)是 funX()元镀,所以绍填,a() 的意思就是 funX()(),也就是funY()栖疑。所以讨永,第一個print出來,是 5 + 1 = 6蔽挠。
那13行的print呢住闯,跟12行有什么區(qū)別呢,他們print出來是一樣的嗎澳淑?
首先要明確一點:在第十三行比原,print括號內(nèi)再一次調(diào)用了a(), 說明程序還是會回去funY()。
回去funY之后杠巡,nonlocal的 x 等于6量窘,所以這一次就return一個7。為什么這個x沒有被初始化呢氢拥?
我請教了羊哥蚌铜,羊哥說:
“這是因為程序有一個 a锨侯,每次print完a之后,a都有一個x的記錄冬殃。所以第二個print 已經(jīng)跟funX里面的x = 5沒有關系了囚痴。只要他一直都是a,print出來的結果就會遞增审葬∩罟觯”
所以我們第12,13涣觉,14行print出來就是 6痴荐,7,8 .
那第15行呢官册,print(b()) 出來會是什么呢生兆。此時,雖然b() 跟 a() 一樣膝宁,代表著FunY()鸦难,但 b()里面沒有x的記錄,所以此時昆汹,初始的x是外部函數(shù)FunX里面的5明刷,那么print(b())出來就是6。
run:
題目2
用閉包的形式满粗,寫出能打印如下結果的函數(shù)辈末。
思路:
首先,題目要求要用閉包的形式映皆。那就說明在內(nèi)部的函數(shù)里面要引用一個外部函數(shù)的變量挤聘。我們仔細觀察一下這個結果。除了'期末成績公布了’這句話捅彻,名字和分數(shù)都是改變的组去,說明他們都是變量。
那我們就把名字作為外部函數(shù)的變量步淹,在內(nèi)部函數(shù)里从隆,便引用這個名字變量,然后再加上一個內(nèi)部函數(shù)的變量缭裆。就可以是一個閉包函數(shù)了键闺。
答案:
在第八行呢,我們讓no1 = final()澈驼,括號里是1號同學的名字辛燥,小羊。
那第九行是什么呢,因為第八行我們說了no1 = final()挎塌,所以no1() 就是 final()()徘六。也就來到了我們的內(nèi)嵌函數(shù)grade。然后括號里呢榴都,就填入我們grade函數(shù)里面的變量score待锈,也就是小羊的語文分數(shù)。
在這里缭贡,如果寫final(‘小羊:’)(語文:98)也是可以的炉擅!但由于我們這里不止一個同學。所以我們才用no1和no2來區(qū)分阳惹。
第十二行呢,意思是再往內(nèi)嵌函數(shù)grade中添加一個變量眶俩。如此一來莹汤,run出來便是前面的樣子了。
這也是內(nèi)包函數(shù)的作用颠印,能很好地將不同對象用同一個函數(shù)處理后的數(shù)據(jù)纲岭,進行整理歸類。
奧利給线罕!