在Python語(yǔ)言中,可以在函數(shù)中定義函數(shù)。 這種在函數(shù)中嵌套定義的函數(shù)也叫內(nèi)部函數(shù)。我們來(lái)看下面的代碼:
創(chuàng)新互聯(lián)長(zhǎng)期為1000+客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為雅安企業(yè)提供專(zhuān)業(yè)的成都網(wǎng)站建設(shè)、網(wǎng)站制作,雅安網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
上述代碼中,定義了函數(shù)greet,在函數(shù)greet內(nèi)部又定義了一個(gè)函數(shù)inner_func, 并調(diào)用該函數(shù)打印了一串字符。
我們可以看到,內(nèi)部函數(shù)inner_func的定義和使用與普通函數(shù)基本相同。需要注意的是變量的作用域,在上述代碼中,函數(shù)參數(shù)name對(duì)于全局函數(shù)greet是局部變量,對(duì)內(nèi)部函數(shù)inner_func來(lái)說(shuō)則是非局部變量。內(nèi)部函數(shù)對(duì)于非局部變量的訪問(wèn)規(guī)則類(lèi)似于標(biāo)準(zhǔn)的外部函數(shù)訪問(wèn)全局變量。
從這個(gè)例子我們還可以看到內(nèi)部函數(shù)的一個(gè)作用,就是通過(guò)定義內(nèi)部函數(shù)的方式將一些功能隱藏起來(lái),防止外部直接調(diào)用。常見(jiàn)的場(chǎng)景是,在一個(gè)復(fù)雜邏輯的函數(shù)中,將一些小的任務(wù)定義成內(nèi)部函數(shù),然后由這個(gè)外層函數(shù)使用,這樣可以使代碼更為清晰,易于維護(hù)。這些內(nèi)部函數(shù)只會(huì)在這個(gè)外層函數(shù)中使用,不能被其他函數(shù)或模塊使用。
在Python語(yǔ)言中, 函數(shù)也是對(duì)象,它可以被創(chuàng)建、賦值給變量,或者作為函數(shù)的返回值。我們來(lái)看下面這個(gè)例子。
在上述代碼中,在函數(shù)gen_greet內(nèi)部定義了inner_func函數(shù),并返回了一個(gè)inner_func函數(shù)對(duì)象。外部函數(shù)gen_greet返回了一個(gè)函數(shù)對(duì)象,所以像gen_greet這樣的函數(shù)也叫工廠函數(shù)。
在內(nèi)部函數(shù)inner_func中,使用了外部函數(shù)的傳參greet_words(非局部變量),以及函數(shù)的參數(shù)name(局部變量),來(lái)打印一個(gè)字符串。
接下來(lái),調(diào)用gen_greet("Hello")創(chuàng)建一個(gè)函數(shù)對(duì)象say_hello,緊接著調(diào)用say_hello("Mr. Zhang"),輸出的結(jié)果為:Hello, Mr. Zhang!
同樣的,調(diào)用gen_greet("Hi")創(chuàng)建一個(gè)函數(shù)對(duì)象say_hi,調(diào)用say_hello("Mr. Zhang"),輸出的結(jié)果為:Hi,Tony!
我們可以發(fā)現(xiàn),gen_greet返回的函數(shù)對(duì)象具有記憶功能,它能夠把所需使用的非局部變量保存下來(lái),用于后續(xù)被調(diào)用的時(shí)候使用。這種保存了非局部變量的函數(shù)對(duì)象被稱(chēng)作閉包(closure)。
那么閉包是如何實(shí)現(xiàn)的呢?其實(shí)并不復(fù)雜,函數(shù)對(duì)象中有一個(gè)屬性__closure__,它就是在創(chuàng)建函數(shù)對(duì)象時(shí)用來(lái)保存這些非局部變量的。
__closure__屬性是一個(gè)元組或者None類(lèi)型。在上述代碼中,我們可以通過(guò)下面方式查看:
函數(shù)的嵌套所實(shí)現(xiàn)的功能大都可以通過(guò)定義類(lèi)的方式來(lái)實(shí)現(xiàn),而且類(lèi)是更加面向?qū)ο蟮拇a編寫(xiě)方式。
嵌套函數(shù)的一個(gè)主要用途是實(shí)現(xiàn)函數(shù)的裝飾器。我們看下面的代碼:
在上述代碼中,logger函數(shù)返回函數(shù)with_logging,with_logging則是打印了函數(shù)func的名稱(chēng)及傳入的參數(shù),然后調(diào)用func, 并將參數(shù)傳遞給func。其中的@wraps(func)語(yǔ)句用于復(fù)制函數(shù)func的名稱(chēng)、注釋文檔、參數(shù)列表等等,使得with_logging函數(shù)具有被裝飾的函數(shù)func相同的屬性。
代碼中接下來(lái)用@logger對(duì)函數(shù)power_func進(jìn)行修飾,它的作用等同于下面的代碼:
可見(jiàn),裝飾器@符其實(shí)就是上述代碼的精簡(jiǎn)寫(xiě)法。
通過(guò)了解了嵌套函數(shù)和閉包的工作原理,我們?cè)谑褂眠^(guò)程中就能夠更加得心應(yīng)手了。
return
會(huì)直接另函數(shù)返回,函數(shù)就運(yùn)行結(jié)束了,所有該函數(shù)體內(nèi)的代碼都不再執(zhí)行了,所以該函數(shù)體內(nèi)的循環(huán)也不可能再繼續(xù)運(yùn)行。
如果你需要讓循環(huán)繼續(xù)執(zhí)行,就不能return函數(shù),而應(yīng)該選用break或者continue。
break:跳出所在的當(dāng)前整個(gè)循環(huán),到外層代碼繼續(xù)執(zhí)行。
continue:跳出本次循環(huán),從下一個(gè)迭代繼續(xù)運(yùn)行循環(huán),內(nèi)層循環(huán)執(zhí)行完畢,外層代碼繼續(xù)運(yùn)行。
return:直接返回函數(shù),所有該函數(shù)體內(nèi)的代碼(包括循環(huán)體)都不會(huì)再執(zhí)行。
一、函數(shù)的定義
函數(shù)是指將一組語(yǔ)句的集合通過(guò)一個(gè)名字(函數(shù)名)封裝起來(lái),想要執(zhí)行這個(gè)函數(shù),只需要調(diào)用函數(shù)名即可
特性:
減少重復(fù)代碼
使程序變得可擴(kuò)展
使程序變得易維護(hù)
二、函數(shù)的參數(shù)
2.1、形參和實(shí)參數(shù)
形參,調(diào)用時(shí)才會(huì)存在的值
實(shí)慘,實(shí)際存在的值
2.2、默認(rèn)參數(shù)
定義:當(dāng)不輸入?yún)?shù)值會(huì)有一個(gè)默認(rèn)的值,默認(rèn)參數(shù)要放到最后
2.3、 關(guān)鍵參數(shù)
定義: 正常情況下,給函數(shù)傳參數(shù)要安裝順序,不想按順序可以用關(guān)鍵參數(shù),只需要指定參數(shù)名即可,(指定了參數(shù)名的就叫關(guān)鍵參數(shù)),但是要求是關(guān)鍵參數(shù)必須放在位置參數(shù)(以位置順序確定對(duì)應(yīng)的參數(shù))之后
2.4、非固定參數(shù)
定義: 如你的函數(shù)在傳入?yún)?shù)時(shí)不確定需要傳入多少個(gè)參數(shù),就可以使用非固定參數(shù)
# 通過(guò)元組形式傳遞
# 通過(guò)列表形式傳遞
# 字典形式(通過(guò)k,value的方式傳遞)
# 通過(guò)變量的方式傳遞
三、函數(shù)的返回值
作用:
返回函數(shù)執(zhí)行結(jié)果,如果沒(méi)有設(shè)置,默認(rèn)返回None
終止函數(shù)運(yùn)行,函數(shù)遇到return終止函數(shù)
四、變量的作用域
全局變量和局部變量
在函數(shù)中定義的變量叫局部變量,在程序中一開(kāi)始定義的變量叫全局變量
全局變量作用域整個(gè)程序,局部變量作用域是定義該變量的函數(shù)
當(dāng)全局變量與局部變量同名是,在定義局部變量的函數(shù)內(nèi),局部變量起作用,其他地方全局變量起作用
同級(jí)的局部變量不能互相調(diào)用
想要函數(shù)里邊的變量設(shè)置成全局變量,可用global進(jìn)行設(shè)置
五、特殊函數(shù)
5.1、嵌套函數(shù)
定義: 嵌套函數(shù)顧名思義就是在函數(shù)里邊再嵌套一層函數(shù)
提示 在嵌套函數(shù)里邊調(diào)用變量是從里往外依次調(diào)用,意思就是如果需要調(diào)用的變量在當(dāng)前層沒(méi)有就會(huì)去外層去調(diào)用,依次內(nèi)推
匿名函數(shù)
基于Lambda定義的函數(shù)格式為: lambda 參數(shù):函數(shù)體
參數(shù),支持任意參數(shù)。
匿名函數(shù)適用于簡(jiǎn)單的業(yè)務(wù)處理,可以快速并簡(jiǎn)單的創(chuàng)建函數(shù)。
# 與三元運(yùn)算結(jié)合
5.3、高階函數(shù)
定義:變量可以指向函數(shù),函數(shù)的參數(shù)可以接收變量,那么一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)稱(chēng)之為高階函數(shù) 只需要滿足一下任意一個(gè)條件,即是高階函數(shù)
接收一個(gè)或多個(gè)函數(shù)作為輸入
return返回另一個(gè)函數(shù)
5.4、遞歸函數(shù)
定義:一個(gè)函數(shù)可以調(diào)用其他函數(shù),如果一個(gè)函數(shù)調(diào)用自己本身,這個(gè)函數(shù)就稱(chēng)為遞歸函數(shù)
在默認(rèn)情況下Python最多能遞歸1000次,(這樣設(shè)計(jì)師是為了防止被內(nèi)存被撐死)可以通過(guò)sys.setrecursionlimit(1500)進(jìn)行修改
遞歸實(shí)現(xiàn)過(guò)程是先一層一層的進(jìn),然后在一層一層的出來(lái)
必須有一個(gè)明確的條件結(jié)束,要不然就是一個(gè)死循環(huán)了
每次進(jìn)入更深層次,問(wèn)題規(guī)模都應(yīng)該有所減少
遞歸執(zhí)行效率不高,遞歸層次過(guò)多會(huì)導(dǎo)致站溢出
# 計(jì)算4的階乘 4x3x2x1
# 打印數(shù)字從1-100
5.5、閉包現(xiàn)象
定義:內(nèi)層函數(shù)調(diào)用外層函數(shù)的變量,并且內(nèi)存函數(shù)被返回到外邊去了
閉包的意義:返回的函數(shù)對(duì)象,不僅僅是一個(gè)函數(shù)對(duì)象,在該函數(shù)外還包裹了一層作用域,這使得,該函數(shù)無(wú)論在何處調(diào)用,優(yōu)先使用自己外層包裹的作用域
嵌套函數(shù)在執(zhí)行時(shí)(而不是在定義時(shí))從父范圍中查找變量。
編譯函數(shù)主體,然后驗(yàn)證“自由”變量(未在函數(shù)本身中通過(guò)賦值定義),然后將其作為閉包單元綁定到函數(shù),并且代碼使用索引引用每個(gè)單元格。pet_function因此具有一個(gè)自由變量(cage),然后將其通過(guò)一個(gè)閉合單元引用,索引為0的閉合本身指向局部變量cage在get_petters功能。
當(dāng)你實(shí)際調(diào)用該函數(shù)時(shí),該閉包將用于在你調(diào)用該函數(shù)時(shí)查看cage周?chē)饔糜蛑械闹怠?wèn)題就在這里。在你調(diào)用函數(shù)時(shí),該函數(shù)已經(jīng)完成了對(duì)其結(jié)果的計(jì)算。將在在執(zhí)行過(guò)程中的一些點(diǎn)局部變量分配各的,和字符串,但在功能的結(jié)束,包含了最后一個(gè)值。因此,當(dāng)你調(diào)用每個(gè)動(dòng)態(tài)返回的函數(shù)時(shí),就會(huì)得到打印的值。get_petterscage'cow''dog''cat'cage'cat''cat'
解決方法是不依賴(lài)閉包。你可以改用部分函數(shù),創(chuàng)建新的函數(shù)作用域或?qū)⒆兞拷壎殛P(guān)鍵字parameter的默認(rèn)值。
部分函數(shù)示例,使用functools.partial():
from functools import partialdef pet_function(cage=None):
print "Mary pets the " + cage.animal + "."yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
創(chuàng)建一個(gè)新的范圍示例:
def scoped_cage(cage=None):
def pet_function():
print "Mary pets the " + cage.animal + "."
return pet_functionyield (animal, partial(gotimes, scoped_cage(cage)))
將變量綁定為關(guān)鍵字參數(shù)的默認(rèn)值:
def pet_function(cage=cage):
print "Mary pets the " + cage.animal + "."yield (animal, partial(gotimes, pet_function))
無(wú)需scoped_cage在循環(huán)中定義函數(shù),編譯僅進(jìn)行一次,而不是在循環(huán)的每次迭代中進(jìn)行。
因?yàn)樽詈蟮哪蔷鋜eturn nested。
tester()()會(huì)自動(dòng)調(diào)用它的返回值,而此時(shí)的返回值為nested,即def nested()這個(gè)函數(shù),所以自然而然執(zhí)行到了里面的print語(yǔ)句。
你可以試試把最后那就return nested改成其他的如return nestedxxx,再tester()()時(shí)就會(huì)報(bào)錯(cuò)了。
另外,在python里對(duì)于方法ester和nested是沒(méi)有tester().nested()這種用法的,所以這樣輸入肯定報(bào)錯(cuò)的,如果ester和nested是類(lèi)(class)的話才有這種寫(xiě)法。
希望對(duì)你有所幫助~~
雖然覺(jué)得這么畸形的邏輯很難有實(shí)際應(yīng)用,大多數(shù)都是誤用。
還是順著去解這個(gè)試了下...
def?fun1(x):
sum=x
def?fun2(y):???????
return?sum+y
def?fun3(z):
nonlocal?sum
sum+=z???????
return?fun2
return?fun3
print(fun1(1)(2)(3))
得6