版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第7章函數(shù)Python程序設(shè)計(jì)第7章函數(shù)計(jì)算機(jī)科學(xué)家尼克勞斯·沃思是好幾種程序語(yǔ)言(Pascal、Modula等)的主設(shè)計(jì)師,從他早在1976年《演算法+數(shù)據(jù)結(jié)構(gòu)=程序》著作的書名中就可以清楚地看出,程序的基本要素是數(shù)據(jù)與操作。Python提供了各種結(jié)構(gòu)的數(shù)據(jù)類型,包括序列與映射,而另一方面,函數(shù)正是把演算法包裝起來予以抽象化的非常重要的機(jī)制,例如內(nèi)置函數(shù)、對(duì)象的方法以及用戶自定義函數(shù),包含了各項(xiàng)主題,如定義、調(diào)用、參數(shù)、返回、遞歸、高階函數(shù),還包括遞歸、裝飾器、函數(shù)式程序設(shè)計(jì),等等。第7章函數(shù)很多人都較為熟悉迭代而討厭遞歸,但若問題本質(zhì)是遞歸,卻采用迭代形式來思考的話,就會(huì)寫出難懂的程序,而且容易有bug。雖然Python提供的工具大都跟迭代相關(guān),但只要加上記憶功能與其他機(jī)制,我們也能輕松運(yùn)用遞歸的程序結(jié)構(gòu)。函數(shù)的定義1函數(shù)的生存周期與作用域4函數(shù)的參數(shù)2函數(shù)的返回值3函數(shù)的遞歸5目錄7.1
函數(shù)的定義7.1
函數(shù)的定義我們?cè)谇懊嫠鶎W(xué)習(xí)的一些程序都只能執(zhí)行一次,例如算出列表元素(成績(jī))的總和,若手上有好幾個(gè)列表,想要讓同一個(gè)程序代碼應(yīng)用于所有這些列表呢?我們需要一種抽象化機(jī)制來包裝程序代碼,在有需要時(shí)可以隨時(shí)調(diào)用它,這個(gè)機(jī)制叫做函數(shù)(function)。定義函數(shù)的語(yǔ)法大致如下:def<函數(shù)名>(<形參)):<函數(shù)體(body)><函數(shù)名>是標(biāo)識(shí)符,<形參>即形式參數(shù),是名稱(標(biāo)識(shí)符)的序列(可能為空)。形參與函數(shù)中使用的所有變量一樣,只能在函數(shù)體中訪問,它與在程序其他地方的同名變量不同。7.1
函數(shù)的定義def語(yǔ)句的作用是定義函數(shù),當(dāng)Python解釋器執(zhí)行def語(yǔ)句時(shí),會(huì)建立類型為function(函數(shù))的對(duì)象,并指定給名稱。使用保留字def開頭,后面接著函數(shù)名稱,然后以括號(hào)包住參數(shù),加上冒號(hào)后,便開始縮排并編寫函數(shù)的主體。從“def”到“:”叫做函數(shù)頭,而里面的程序代碼稱為函數(shù)體,函數(shù)結(jié)束時(shí)可使用“return語(yǔ)句”將結(jié)果返回給調(diào)用方?!境绦?qū)嵗?-1】傳入含有成績(jī)的列表,算出成績(jī)總分之后返回的函數(shù)。defmy_sum(numbers): #函數(shù)頭,函數(shù)名稱與參數(shù)
total=0 #負(fù)責(zé)存儲(chǔ)總分,這是個(gè)“局部”名稱
forxinnumbers: #以for循環(huán)算總分
total+=xreturntotal #以return語(yǔ)句返回總分7.1
函數(shù)的定義7.1
函數(shù)的定義函數(shù)定義完成后,最基本的使用方式是“調(diào)用”,把適當(dāng)?shù)膶?duì)象傳給它作為參數(shù),然后等待接收函數(shù)返回的對(duì)象,函數(shù)調(diào)用屬于表達(dá)式。調(diào)用時(shí),先寫出函數(shù)名稱,后面跟著的括號(hào)“()”代表你想調(diào)用該函數(shù),在括號(hào)內(nèi)傳入適當(dāng)?shù)膮?shù)(實(shí)參,即實(shí)際參數(shù))來執(zhí)行那組程序代碼。scores0=[60,73,81,95,34]scores1=[10,20,30,40,50,60]total0=my_sum(scores0) #調(diào)用函數(shù),并把返回對(duì)象賦值給名稱total0total1=my_sum(scores1) #把程序代碼寫成函數(shù)后,便可一再地運(yùn)用7.1
函數(shù)的定義讓我們?cè)賹憘€(gè)能計(jì)算list(或是tuple、str)對(duì)象長(zhǎng)度的函數(shù):defmy_len(seq): #seq可以是列表、數(shù)組、字符串
n=0forxinseq:n+=1 #有幾個(gè)元素便加幾次1returnn #返回長(zhǎng)度函數(shù)定義與函數(shù)調(diào)用是兩個(gè)獨(dú)立的概念,分別代表不同的動(dòng)作,如果只執(zhí)行def語(yǔ)句,僅會(huì)建立函數(shù)對(duì)象,并不會(huì)執(zhí)行函數(shù)體內(nèi)的程序代碼。7.1
函數(shù)的定義歸納一下Python的函數(shù):(1)函數(shù)是一種子程序。使用函數(shù)可以減少代碼重復(fù),并用于組織或模塊化程序。一旦定義了函數(shù),就可以從程序中的不同位置被多次調(diào)用。參數(shù)允許函數(shù)具有可更改的部分,函數(shù)定義中出現(xiàn)的參數(shù)稱為形參,函數(shù)調(diào)用中出現(xiàn)的表達(dá)式稱為實(shí)參。7.1
函數(shù)的定義(2)Python對(duì)函數(shù)的調(diào)用是一個(gè)四步過程:第一步,調(diào)用程序在調(diào)用點(diǎn)暫停執(zhí)行。第二步,實(shí)參將其值賦給形參。第三步,執(zhí)行函數(shù)體。第四步,控制返回到函數(shù)被調(diào)用之后的點(diǎn),函數(shù)返回的值作為表達(dá)式結(jié)果。7.1
函數(shù)的定義(3)變量的作用域是程序可以引用它的區(qū)域。函數(shù)定義中的形參和其他變量是函數(shù)的局部變量。(4)函數(shù)可以通過返回值將信息傳遞回調(diào)用者。在Python中,函數(shù)可以返回多個(gè)值。返回值的函數(shù)通常應(yīng)該從表達(dá)式內(nèi)部調(diào)用。沒有顯式返回值的函數(shù)會(huì)返回特殊對(duì)象None。(5)Python按值傳遞參數(shù)。如果傳遞的值是可變對(duì)象,則對(duì)象所做的更改對(duì)調(diào)用者可見。之前編寫的程序只包含一個(gè)函數(shù),通常就是main。我們還使用了預(yù)先編寫的函數(shù)和方法,包括內(nèi)置的Python函數(shù)(如print、abs)、來自Python標(biāo)準(zhǔn)庫(kù)的函數(shù)和方法(如math.sqrt)。函數(shù)是構(gòu)建復(fù)雜程序的重要工具。7.1
函數(shù)的定義可以將函數(shù)想象成是一個(gè)“子程序”,其基本思想是寫一個(gè)語(yǔ)句序列,并給這個(gè)序列取一個(gè)名字,然后可以通過引用函數(shù)名稱,在程序中的任何位置執(zhí)行這些指令。創(chuàng)建函數(shù)的程序部分稱為“函數(shù)定義”。當(dāng)函數(shù)隨后在程序中使用時(shí),稱該定義被“調(diào)用”。單個(gè)函數(shù)定義可以在程序的許多不同位置被調(diào)用。7.1
函數(shù)的定義例如,假設(shè)我們希望編寫一個(gè)程序,打印“HappyBirthday”的歌詞,歌詞是這樣的:Happybirthdaytoyou!Happybirthdaytoyou!Happybirthday,dear<insert-name>.Happybirthdaytoyou!7.1
函數(shù)的定義這個(gè)問題的一個(gè)簡(jiǎn)單方法是使用四個(gè)print語(yǔ)句。下面的交互式會(huì)話創(chuàng)建了一個(gè)程序,對(duì)小明(xiaoming)唱“HappyBirthday”。>>>defmain():print("Happybirthdaytoyou!")print("Happybirthdaytoyou!")print("Happybirthday,dearxiaoming,")print("Happybirthdaytoyou!")7.1
函數(shù)的定義可以運(yùn)行這個(gè)程序,得到歌詞。下面我們引入一個(gè)函數(shù),打印第一、第二和第四行歌詞。>>>defhappy():print("Happybirthdaytoyou!")這里定義了一個(gè)名為happy的函數(shù),調(diào)用它會(huì)使Python打印一行歌詞。現(xiàn)在我們用happy來為xiaoming重寫歌詞。>>>defsingXM():happy()happy()print("Happybirthday,dearxiaoming.")happy()這個(gè)版本打的字要少得多。試運(yùn)行這個(gè)程序。7.1
函數(shù)的定義假設(shè)今天也是小芳(xiaofang)的生日,我們同樣也可以為小芳準(zhǔn)備一個(gè)程序(singXF())?,F(xiàn)在寫一個(gè)主程序,送給小明和小芳:>>>defmain():singXM()print()singXF()兩個(gè)函數(shù)調(diào)用之間的print在輸出的歌詞之間留出空行。運(yùn)行這個(gè)最終產(chǎn)品。7.1
函數(shù)的定義singXM和singXF兩個(gè)函數(shù)幾乎相同,唯一區(qū)別是第三個(gè)print語(yǔ)句結(jié)束時(shí)的姓名。于是,我們可以通過使用“參數(shù)”將這兩個(gè)函數(shù)并在一起。讓我們寫一個(gè)名為sing的通用函數(shù):>>>defslng(person):happy()happy()print("HappyBirthday,dear",person+".")happy()此函數(shù)用到了名為person的參數(shù)。參數(shù)是在調(diào)用函數(shù)時(shí)初始化的變量。如果需要為浩浩(haohao)打印歌詞,這只需要在調(diào)用函數(shù)時(shí)提供名稱作為參數(shù): >>>sing("haohao")【程序?qū)嵗?-2】作為模塊文件的完整程序。#happy.pydefhappy():print("HappyBirthdaytoyou!")defsing(person):happy()happy()print("Happybirthday,dear",person+".")happy()7.1
函數(shù)的定義defmain():sing("xiaoming")print()sing("xiaofang")print()sing("haohao")main()7.1
函數(shù)的定義7.1
函數(shù)的定義函數(shù)作為減少代碼重復(fù)的機(jī)制,可以縮短和簡(jiǎn)化程序。在實(shí)踐中,即使函數(shù)實(shí)際上讓程序更長(zhǎng),人們也會(huì)經(jīng)常使用,這就是使用函數(shù)的另一個(gè)原因:讓程序更模塊化。由于你設(shè)計(jì)的算法越來越復(fù)雜,因此理解程序也越來越難。處理這種復(fù)雜性的一種方法是將算法分解成更小的子程序,每個(gè)子程序自身都有意義。7.2函數(shù)的參數(shù)位置參數(shù)與關(guān)鍵字參數(shù)形參與“*”和“**”實(shí)參與“*”和“**”7.2
函數(shù)的參數(shù)每個(gè)函數(shù)本身都是子程序。在一個(gè)函數(shù)內(nèi)部使用的變量是該函數(shù)的“局部”變量。函數(shù)要看到另一個(gè)函數(shù)中的變量,唯一方法是將該變量作為參數(shù)傳入。調(diào)用函數(shù)時(shí)“傳入?yún)?shù)”這個(gè)動(dòng)作跟“賦值”幾乎一模一樣,換句話說,所謂傳入?yún)?shù),就是調(diào)用方把某個(gè)對(duì)象賦值給參數(shù)的名稱罷了。要小心的是,如果參數(shù)是個(gè)可變對(duì)象,那么函數(shù)拿到該參數(shù)后,也可以改變?cè)搶?duì)象。7.2
函數(shù)的參數(shù)回到HappyBirthday的例子,讓我們追蹤唱兩次歌詞的過程。下面是main函數(shù)體的一部分:sing("xiaoming")print()sing("xiaofang")7.2
函數(shù)的參數(shù)Python遇到sing("xiaoming")時(shí),main暫停執(zhí)行。在這里,Python查找sing的定義,并且看到它具有單個(gè)形參person。形參被賦予實(shí)參的值,所以這就好像我們執(zhí)行了下面的語(yǔ)句: person="xiaoming"在這里,Python開始執(zhí)行sing的函數(shù)體。第一個(gè)語(yǔ)句是happy函數(shù)調(diào)用,Python暫停執(zhí)行sing并將控制傳遞給被調(diào)用的函數(shù)。happy的函數(shù)體包含一個(gè)print。這個(gè)語(yǔ)句被執(zhí)行,然后返回到它離開的地方。7.2
函數(shù)的參數(shù)執(zhí)行以這種方式繼續(xù),Python又繞路去了兩次happy,完成了sing的執(zhí)行。當(dāng)Python到達(dá)sing的末尾時(shí),控制就返回到main,并在函數(shù)調(diào)用之后緊接著繼續(xù)。函數(shù)完成時(shí),會(huì)回收局部函數(shù)變量占用的內(nèi)存,不保留局部變量。下一個(gè)要執(zhí)行的語(yǔ)句是main中的不帶參數(shù)print語(yǔ)句,這將在輸出中形成空行。然后Python調(diào)用另一個(gè)sing,控制轉(zhuǎn)移到函數(shù)定義,這次形參是“xiaofang”。最后,針對(duì)xiaofang執(zhí)行sing的函數(shù)體,并且在函數(shù)調(diào)用的點(diǎn)之后控制返回main,到達(dá)代碼片段的底部。7.2
函數(shù)的參數(shù)通常,當(dāng)函數(shù)定義具有多個(gè)參數(shù)時(shí),實(shí)參按位置與形參匹配。第一個(gè)實(shí)參分配給第一個(gè)形參,第二個(gè)實(shí)參分配給第二個(gè)形參,以此類推??梢岳藐P(guān)鍵字參數(shù)修改此行為,這些參數(shù)通過名稱匹配(如調(diào)用print中的end="")。7.2.1位置參數(shù)與關(guān)鍵字參數(shù)Python提供了豐富靈活的方式讓我們來指定形參與實(shí)參之間的配對(duì)關(guān)系,能以“位置”與“關(guān)鍵字”來指定、可預(yù)先記錄參數(shù)的缺省值、也能使用tuple與dict參數(shù),收集額外多余的位置參數(shù)與關(guān)鍵字參數(shù)。傳入?yún)?shù)時(shí),最基本的重點(diǎn)是必須讓每個(gè)形式參數(shù)都能有值(指向某對(duì)象),如若不然則發(fā)生錯(cuò)誤。雖然調(diào)用函數(shù)傳入?yún)?shù)時(shí)可使用位置參數(shù)與關(guān)鍵字參數(shù),但其實(shí)Python底層的處理機(jī)制只有位置參數(shù)而已,最后,每個(gè)形參都必須對(duì)應(yīng)到某個(gè)實(shí)參,也就是函數(shù)的參數(shù)名稱必須指向某對(duì)象,若參數(shù)太多或太少,都會(huì)發(fā)生錯(cuò)誤。7.2.1位置參數(shù)與關(guān)鍵字參數(shù)函數(shù)定義后,Python就知道有幾個(gè)形參,從左到右排列;調(diào)用函數(shù)、傳入實(shí)參時(shí),Python會(huì)先從左到右拿出位置參數(shù),逐一放到對(duì)應(yīng)形參的位置上,然后再處理關(guān)鍵字參數(shù),比對(duì)關(guān)鍵字與形參名稱,擺放到相對(duì)應(yīng)的位置。>>>deff(x,y,z):print(x,y,z)#3個(gè)形參,從左到右是x、y、z>>>f(1,2,3) #實(shí)參是3個(gè)位置參數(shù)123>>>f(1,2,z=3) #2個(gè)位置參數(shù),擺放到x、y,123 #1個(gè)關(guān)鍵字參數(shù),擺放到z>>>f(x=1,y=2,z=3) #3個(gè)關(guān)鍵字參數(shù),比對(duì)后擺放到x、y、z123>>>f(z=3,x=1,y=2) #關(guān)鍵字參數(shù)會(huì)比對(duì)名稱,不管順序,123 #但這種寫法容易搞混,不建議7.2.1位置參數(shù)與關(guān)鍵字參數(shù)>>>f(1,2,3,4) #4個(gè)位置參數(shù),太多Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:f()takes3positionalargumentsbut4weregiven>>>f(1,x=11,y=2,z=3) #x參數(shù)有兩個(gè)值,太多Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:f()gotmultiplevaluesforargument'x'7.2.1位置參數(shù)與關(guān)鍵字參數(shù)>>>f(1,2) #2個(gè)位置參數(shù),數(shù)量不足Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:f()missing1requiredpositionalargument:'z'>>>f(y=2,z=3) #2個(gè)關(guān)鍵字參數(shù),數(shù)量不足Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:f()missing1requiredpositionalargument:'x'>>>f(y=2,z=3,1) #關(guān)鍵字參數(shù)之后不能有位置參數(shù)
File"<stdin>",line1SyntaxError:non-keywordargafterkeywordarg7.2.1位置參數(shù)與關(guān)鍵字參數(shù)7.2.1位置參數(shù)與關(guān)鍵字參數(shù)定義函數(shù)時(shí)可為參數(shù)指定缺省值,函數(shù)對(duì)象會(huì)記錄缺省值(對(duì)象),當(dāng)調(diào)用方?jīng)]有為某個(gè)式參指定實(shí)參時(shí),便由缺省值提供。當(dāng)然,參數(shù)也不能太多或太少。>>>deff(x,y=2,z):print(x,y,z)#缺省值參數(shù)之后不能有非缺省值參數(shù)...File"<stdin>",line1SyntaxError:non-defaultargumentfollowsdefaultargument>>>deff(x,y=2,z=3):print(x,y,z)...7.2.1位置參數(shù)與關(guān)鍵字參數(shù)>>>f(1) #1個(gè)位置參數(shù),擺放到x,123 #y與z由缺省值提供>>>f(1,22) #2個(gè)位置參數(shù),擺放到x、y,1223 #z由缺省值提供>>>f(x=1) #1個(gè)關(guān)鍵字參數(shù),擺放到x,123 #y與z由缺省值提供>>>f(x=1,z=33) #2個(gè)關(guān)鍵字參數(shù),擺放到x、z1233 #y由缺省值提供>>>f(y=22)#1個(gè)關(guān)鍵字參數(shù),擺放到y(tǒng),z由缺省值提供,但仍缺少x7.2.1位置參數(shù)與關(guān)鍵字參數(shù)7.2.2形參與“*”和“**”有時(shí)我們需要傳入不定個(gè)數(shù)的參數(shù),為此,可在定義函數(shù)時(shí),在某形參名稱之前加上星號(hào)“*”,那么該形參缺省為空的tuple對(duì)象,額外的位置參數(shù)都會(huì)被放進(jìn)此tuple;而若在某形參名稱之前加上雙星號(hào)“**”,該形參缺省為空的dict對(duì)象,額外的關(guān)鍵字參數(shù)都會(huì)被放進(jìn)此dict。形參星號(hào)“*”用法的一般語(yǔ)法是: deff(pos1,pos2,...,posN,*t)其中pos1、pos2到posN都是形參的名稱,同樣的,實(shí)參會(huì)被逐一擺放,若有額外的實(shí)參,通通會(huì)被放進(jìn)tuple對(duì)象t里。>>>deff(p1,p2,p3,*t):print(p1,p2,p3,t)...>>>f(1,2) #參數(shù)不夠>>>f(1,2,3,4,5) #額外的位置參數(shù)被放進(jìn)星號(hào)參數(shù)t>>>f(1,2,3) #參數(shù)剛剛好,t缺省為空tuple對(duì)象>>>f(p1=1,p2=2,p3=3,4) #以關(guān)鍵字參數(shù)指定,但關(guān)鍵字參數(shù)之后不能有位置參數(shù)7.2.2形參與“*”和“**”7.2.3實(shí)參與“*”和“**”形參可以加上星號(hào)和雙星號(hào),調(diào)用函數(shù)時(shí),實(shí)參也能加上“*”和“**”。加上“*”的實(shí)參必須是個(gè)可迭代項(xiàng),它提供的元素會(huì)成為位置參數(shù);而加上“**”的實(shí)參必須是個(gè)映射對(duì)象(例如字典),它負(fù)責(zé)提供關(guān)鍵字參數(shù)。>>>deff(p1,p2,p3,p4,p5):#5個(gè)...print(p1,p2,p3,p4,p5)...>>>f(11,22,33,*range(2)) #3個(gè)位置參數(shù),range(2)是個(gè)可迭代項(xiàng),11223301 #加上星號(hào)解開,提供2個(gè)位置參數(shù)>>>f(11,22,33,range(2))#注意,這會(huì)是4個(gè)參數(shù),3個(gè)整數(shù)對(duì)象與1個(gè)可迭代項(xiàng)>>>f(*range(2),11,22,33) #出錯(cuò),星號(hào)加實(shí)參之后只能接關(guān)鍵字參數(shù)>>>m={'k1':'a','k2':'b'} #映射對(duì)象>>>deff(p1,p2,p3,k1,k2): #5個(gè)...print(p1,p2,p3)...print(k1,k2)...7.2.3實(shí)參與“*”和“**”>>>f(1,2,3,**m) #3個(gè)位置參數(shù),其余由m提供,123 #加上雙星號(hào)解開m,傳入k1與k2ab>>>m={'k1':'a','k2':'b','k3':'c'}>>>f(1,2,3,**m)#如果m里的東西太多,出錯(cuò)7.2.3實(shí)參與“*”和“**”7.2.3實(shí)參與“*”和“**”其實(shí)概念并不困難,只要分清楚“*”和“**”出現(xiàn)在形參(函數(shù)定義)時(shí),代表的是“收集”額外的參數(shù),而出現(xiàn)在實(shí)參(函數(shù)調(diào)用)時(shí),則具有“解開”的意義。如果互相結(jié)合,變化更多了,但傳入?yún)?shù)的基本機(jī)制仍相同。>>>deff(p1,p2,p3,*t):print(p1,p2,p3,t)...>>>f(11,22,33,*range(2)) #總共5個(gè)位置參數(shù)112233(0,1) #0、1進(jìn)入t>>>f(11,22,*range(2)) #總共4個(gè)位置參數(shù)11220(1,) #1進(jìn)入t>>>deff(p1,p2,p3,*t,k1,k2,**d):...print(p1,p2,p3,t)...print(k1,k2)...print(d)...7.2.3實(shí)參與“*”和“**”>>>it=range(3)>>>m={'k1':'a','k2':'b','k3':'c'}>>>f(11,22,*it,**m) #p1、p2、p3得到11、22、011220(1,2) #it剩下的元素1、2,進(jìn)入tab #m有k1與k2{'k3':'c'} #m剩下的元素k3,進(jìn)入d7.2.3實(shí)參與“*”和“**”7.2.3實(shí)參與“*”和“**”可見,參數(shù)的學(xué)問很大,語(yǔ)法很復(fù)雜,但只要記住基本規(guī)則,再記住其中好用、常用的那幾種就夠了??梢钥纯碢ython內(nèi)置函數(shù)的形參,當(dāng)作編寫函數(shù)制定形參時(shí)的參考對(duì)象。7.3
函數(shù)的返回值7.3
函數(shù)的返回值事實(shí)上,函數(shù)的基本思想和詞匯是從數(shù)學(xué)中借用的,可以看作是輸入變量和輸出變量之間的關(guān)系。在數(shù)學(xué)上,函數(shù)調(diào)用實(shí)際上是一個(gè)產(chǎn)生結(jié)果的表達(dá)式。在Python中,參數(shù)傳遞提供了一種初始化函數(shù)中變量的機(jī)制。從某種意義上說,參數(shù)是函數(shù)的輸入。我們可以調(diào)用一個(gè)函數(shù)多次,并通過更改輸入?yún)?shù)獲得不同的結(jié)果。通常我們還希望從函數(shù)中獲取信息。7.3
函數(shù)的返回值Python函數(shù)被用作一種新的命令形式,被調(diào)用來執(zhí)行命令。例如,考慮從math庫(kù)調(diào)用sqrt函數(shù): discRt=math.sqrt(b*b–4*a*c)這里b*b–4*a*c的值是math.sqrt函數(shù)的實(shí)參。由于函數(shù)調(diào)用發(fā)生在賦值語(yǔ)句的右側(cè),這意味著它是一個(gè)表達(dá)式。math.sqrt函數(shù)生成一個(gè)值,然后將該值賦給變量discRt。技術(shù)上,我們說sqrt返回其參數(shù)的平方根。7.3
函數(shù)的返回值編寫返回值的函數(shù)非常容易。下面是一個(gè)函數(shù)的Python實(shí)現(xiàn),返回其參數(shù)的平方:defsquare(x):returnx**2這個(gè)例子中,Python函數(shù)的主體由一個(gè)return語(yǔ)句組成。當(dāng)Python遇到return時(shí),它立即退出當(dāng)前函數(shù),并將控制返回到函數(shù)被調(diào)用之后的點(diǎn)。此外,return語(yǔ)句中提供的值作為表達(dá)式結(jié)果發(fā)送回調(diào)用者。本質(zhì)上,這只是為前面提到的四步函數(shù)調(diào)用過程添加了一個(gè)小細(xì)節(jié):函數(shù)的返回值用作表達(dá)式的結(jié)果。效果就是,可以在代碼中任何可以合法使用表達(dá)式的地方使用square函數(shù)。下面是一些交互示例:>>>square(3)9>>>print(square(4))16>>>x=5>>>y=square(x)>>>print(y)25>>>print(square(x)+square(3))347.3
函數(shù)的返回值7.3
函數(shù)的返回值程序中函數(shù)定義的順序并不重要,只要確保在程序?qū)嶋H運(yùn)行函數(shù)之前定義該函數(shù)。例如,如果讓main函數(shù)在頂部定義同樣能工作,因?yàn)橹钡侥K的最后一行才會(huì)發(fā)生main()調(diào)用,而這時(shí)所有函數(shù)己被定義。回到HappyBirthday程序。在最初的版本中,我們使用了幾個(gè)包含print語(yǔ)句的函數(shù)。我們可以不讓輔助函數(shù)執(zhí)行打印,而是簡(jiǎn)單地讓它們返回值(在這個(gè)例子中是字符串),然后由main打印。請(qǐng)考慮下面這個(gè)版本的程序?!境绦?qū)嵗?-3】改進(jìn)的HappyBirthday程序。#happy2.pydefhappy():return"HappyBirthdaytoyou!\n"defverseFor(person):lyrics=happy()*2+"Happybirthday,dear"+person+".\n"+happy()returnlyricsdefmain():forpersonin["xiaoming","xiaofang","haohao"]:print(verseFor(person))main()7.3
函數(shù)的返回值7.3
函數(shù)的返回值注意,所有的打印都在一個(gè)地方(main函數(shù)中)進(jìn)行,而happy和verseFor只負(fù)責(zé)創(chuàng)建和返回適當(dāng)?shù)淖址?。利用函?shù)返回值的功能精簡(jiǎn)了程序,讓整個(gè)句子建立在單個(gè)字符串表達(dá)式中。請(qǐng)仔細(xì)查看并理解這行代碼。這個(gè)版本的程序比原來的更靈活,因?yàn)榇蛴〔辉俜植荚诙鄠€(gè)函數(shù)中。例如,我們可以輕松地修改程序,將結(jié)果寫入文件而不是屏幕。我們要做的是打開一個(gè)文件進(jìn)行寫入,并在print語(yǔ)句中添加一個(gè)“file=”參數(shù)。不需要修改其他函數(shù)。下面是完整的修改:defmain():outf=open("Happy_Birthday.txt","W")forpersonin["xiaoming","xiaofang","haohao"]:print(verseFor(person),file=outf)outf.close()7.3
函數(shù)的返回值通常,讓函數(shù)返回值,而不是將信息打印到屏幕上,這樣,調(diào)用者可以選擇是打印信息還是將它用于其他用途。7.3
函數(shù)的返回值有時(shí)一個(gè)函數(shù)需要返回多個(gè)值。這可以通過在return語(yǔ)句中簡(jiǎn)單地列出多個(gè)表達(dá)式來完成。下面是一個(gè)計(jì)算兩個(gè)數(shù)字的和與差的函數(shù):defsumDiff(x,y):sum=x+ydiff=x–yreturnsum,diff這個(gè)return傳遞回兩個(gè)值。調(diào)用這個(gè)函數(shù)時(shí),我們將它放在一個(gè)同時(shí)賦值中:num1,num2=input("Pleaseentertwonumbers(num1,num2)").split(",")s,d=sumDiff(float(num1),float(num2))print("TheSumis",s,"andthedifferenceis",d)7.3
函數(shù)的返回值與參數(shù)一樣,從函數(shù)返回多個(gè)值時(shí),它們根據(jù)位置賦給變量。在這個(gè)例子中,s將獲得return列出的第一個(gè)值(sum),d將獲得第二個(gè)值(diff)。從技術(shù)上講,Python中的所有函數(shù)都返回一個(gè)值,而不管函數(shù)實(shí)際上是否包含return語(yǔ)句。沒有return的函數(shù)總是返回一個(gè)特殊對(duì)象,表示為None。這個(gè)對(duì)象通常用作變量的一種默認(rèn)值,如果它當(dāng)前沒有指向任何有用的對(duì)象。調(diào)用函數(shù)進(jìn)入函數(shù)體,便會(huì)逐一執(zhí)行其內(nèi)部的程序代碼,若執(zhí)行到末端便會(huì)結(jié)束,回到調(diào)用方。另外,通常會(huì)使用return語(yǔ)句把執(zhí)行結(jié)果(對(duì)象)返回給調(diào)用方;一個(gè)函數(shù)內(nèi)的return語(yǔ)句可以有多個(gè)。若return后面沒有東西、或者根本沒有return語(yǔ)句,那么函數(shù)結(jié)束時(shí)會(huì)返回None。雖然return只能返回一個(gè)對(duì)象,但可返回tuple對(duì)象,所以可以把許多個(gè)對(duì)象放在tuple里,然后返回。7.3
函數(shù)的返回值例如,之前算總分與求平均成績(jī)的程序代碼,可寫成如下的函數(shù):deftotal_avg(scores,initial=0):n=0total=initialforxinscores: #以for循環(huán)計(jì)算總分
total+=xn+=1 #也順便得知長(zhǎng)度
return(total,total/n) #返回含有總分與平均成績(jī)的數(shù)組建立數(shù)組的括號(hào)“()”通??墒÷?,所以可改寫為“returntotal,total/n”。7.4函數(shù)的生存周期與作用域第一次指定名稱同名問題del語(yǔ)句7.4
函數(shù)的生存周期與作用域前面學(xué)習(xí)的程序代碼一旦產(chǎn)生某名稱后,就一直使用、直到程序結(jié)束。但“del語(yǔ)句”可以刪除名稱,連帶刪除該名稱與指向?qū)ο笾g的綁定關(guān)系。>>>a=3 #名稱a指向int對(duì)象3>>>a+=1 #名稱a轉(zhuǎn)而指向新的int對(duì)象4,3變成垃圾>>>dela #使用del語(yǔ)句刪除名稱a,也刪除與對(duì)象之間的綁定關(guān)系7.4
函數(shù)的生存周期與作用域“對(duì)象”的作用域(又稱可視范圍)實(shí)際上是“名稱”的作用域,決定了在程序的什么地方能夠看到該名稱,進(jìn)而存取該名稱指向的對(duì)象;這取決于該名稱定義的地方(第一次賦值)。如果名稱定義在程序文件最外層的地方,例如下面例子中的a、b、foo、x、y,一旦定義后,任何地方都看得到這些名稱,也能存取名稱指向的對(duì)象,簡(jiǎn)稱“全局變量”;而定義在函數(shù)內(nèi)的名稱(包括參數(shù)),如下面的n與m,只擁有“局部”作用域,只有在該函數(shù)內(nèi)才看得到這些名稱,簡(jiǎn)稱“局部變量”。a,b=3,4 #名稱a與b擁有全局作用域deffoo(n): #名稱foo擁有全局作用域
m=5 #n與m,局部作用域
returnn+m+a #在函數(shù)里都可存取x=foo(b) #任何地方都可存取全局變量b,y=a+n #這一行會(huì)發(fā)生錯(cuò)誤,因?yàn)榇颂幙床坏絥7.4
函數(shù)的生存周期與作用域如果在函數(shù)內(nèi)定義的局部變量與全局變量相同時(shí),那么在函數(shù)內(nèi)調(diào)用該名稱時(shí),實(shí)際存取的是局部變量(指向的對(duì)象),而非全局變量(指向的對(duì)象),也就是說同名的局部變量會(huì)屏蔽全局變量。a=10 #全局變量adeffoo(n):a=100 #產(chǎn)生局部變量a,不是賦值新對(duì)象給全局變量areturnn+a#a是局部變量ax=foo(5) #x會(huì)是105,不是157.4
函數(shù)的生存周期與作用域在函數(shù)(局部)里雖然可以自由調(diào)用全局變量,但只能讀、不能寫。一旦在函數(shù)里把某全局變量放在賦值語(yǔ)句的左邊,就會(huì)產(chǎn)生局部變量,而不是把新對(duì)象賦值給全局變量。為了把新對(duì)象賦值給全局變量,辦法是使用“global語(yǔ)句”。a=10 #全局變量adeffoo(n):globala #以global語(yǔ)句宣告a是個(gè)全局變量
a=100 #全局變量a轉(zhuǎn)而指向新對(duì)象100returnn+ax=foo(5) #x會(huì)是105y=a #y會(huì)是1007.4.1第一次指定名稱為了進(jìn)一步了解函數(shù)的定義與調(diào)用,也為了后面介紹的遞歸與高階函數(shù),我們來進(jìn)一步了解作用域與命名空間。作用域分為內(nèi)置、全局、局部三種。在Python程序里,名稱的作用域取決于它“第一次指定”的位置。無須實(shí)際執(zhí)行,光看程序代碼就能判斷得知某名稱的作用域,我們稱呼這種機(jī)制為靜態(tài)范圍。同樣,當(dāng)你寫上某名稱(想存取它指向的對(duì)象)時(shí),光看程序代碼就能得知存取到的會(huì)是哪個(gè)范圍里的名稱。li=[0,1,2,3,4] #第一次指定名稱li于全局范圍,指向列表對(duì)象a=3 #第一次指定名稱a,指向整數(shù)對(duì)象3a=len(li) #名稱a位于全局范圍,在全局范圍內(nèi)沒有名稱len,
#到內(nèi)置范圍去找,找到了內(nèi)置函數(shù)lenb=sum(li) #第一次指定名稱b,名稱sum位于內(nèi)置范圍,li在全局deffoo_1(a,b): #第一次指定名稱foo_1,指向新建立的函數(shù)對(duì)象,
d=a+b-c #a、b、c都是在局部(函數(shù))范圍內(nèi)第一次指定,
returnd #局部名稱a與b,掩蓋住全局名稱a與bdeffoo_2(a,b): #第一次指定名稱foo_2,指向新建立的函數(shù)對(duì)象,
defbar(x): #在局部(函數(shù))范圍內(nèi)也可有def語(yǔ)句,名稱barreturnx**2 #位于foo_2的局部范圍內(nèi),不在全局范圍7.4.1第一次指定名稱len=a*b+bar(c) #在局部范圍內(nèi)第一次指定名稱len,掩蓋住內(nèi)置函數(shù)lenreturnlenc=5m=foo_1(a,b) #在全局范圍找到名稱foo_1,是個(gè)函數(shù)n=foo_2(a,b+2+len(li))#在內(nèi)置范圍找到名稱len#bar(3) #若執(zhí)行會(huì)發(fā)生“找不到名稱bar”的錯(cuò)誤7.4.1第一次指定名稱7.4.1第一次指定名稱規(guī)則整理如下:(1)名稱第一次指定的地方,決定了該名稱位于哪個(gè)范圍。(2)名稱的定義過程,也就是以名稱找出指向?qū)ο蟮倪^程,會(huì)依照局部、全局、內(nèi)置范圍的順序。(3)若不同范圍里的名稱同名,那么在函數(shù)內(nèi),局部名稱會(huì)掩蓋住全局名稱或內(nèi)置名稱;在全局范圍內(nèi),全局名稱會(huì)掩蓋住內(nèi)置名稱。(4)函數(shù)的局部范圍各自獨(dú)立,所以函數(shù)A與函數(shù)B的參數(shù)即使同名,也不會(huì)起沖突。7.4.2同名問題因?yàn)樵诤瘮?shù)內(nèi)第一次指定某名稱時(shí)就會(huì)在局部范圍內(nèi)產(chǎn)生該名稱,也會(huì)掩蓋住同名的全局名稱(如果有的話)。當(dāng)你想要在函數(shù)里重新指定全局名稱時(shí),必須使用global語(yǔ)句來聲明。y=3 #全局名稱ydeff(x):y=x**2 #這是局部名稱yprint(y) #輸出局部名稱y,值是9f(3)print(y) #輸出全局名稱y,值是3########y=3deff(x):globaly #以global宣告y是個(gè)全局名稱
y=x**2 #重新指定
print(y) #輸出全局名稱y,值是9f(3)print(y) #輸出全局名稱y,值是97.4.2同名問題7.4.2同名問題在函數(shù)里的局部名稱可由global語(yǔ)句聲明位于全局范圍,所以,也能在函數(shù)調(diào)用后才建立全局名稱的綁定關(guān)系,但這是不好的寫法。#此時(shí)尚無全局名稱ydeffoo(x):globaly #宣告y為全局名稱
y=x #此時(shí)才指定#print(y)#若執(zhí)行這一行會(huì)出現(xiàn)錯(cuò)誤”全局名稱y尚未定義”foo(3)#必須調(diào)用此函數(shù),然后y才綁定某對(duì)象print(y)#輸出全局名稱y,值為37.4.3del語(yǔ)句顧名思義,del語(yǔ)句的作用就是刪除名稱,連帶地也會(huì)刪除名稱與對(duì)象之間的綁定關(guān)系,若對(duì)象已無名稱指向它,便成為垃圾等待回收。del語(yǔ)句在全局范圍內(nèi)會(huì)刪除全局名稱,而在局部范圍內(nèi)會(huì)刪除局部名稱。y=999dely #刪除全局名稱yx=3deffoo():globalx #聲明x為全局名稱
delx #刪除foo()print(x) #錯(cuò)誤,找不到x7.4.3del語(yǔ)句提醒一下,若在函數(shù)內(nèi)有del語(yǔ)句,且名稱未以global聲明,該名稱將會(huì)被視為區(qū)域名稱,連帶影響其他的程序語(yǔ)句。x=3deffoo():print(x) #若無下面的delx,這一行會(huì)輸出3,
delx #若有delx,x被視為局部名稱,上一行就會(huì)出錯(cuò)foo()應(yīng)該盡量避免在局部范圍內(nèi)存取全局名稱,這么做能提高程序的可維護(hù)性,易讀易懂。7.5函數(shù)的遞歸遞歸的概念實(shí)例:漢諾塔7.5
遞歸Python語(yǔ)言強(qiáng)調(diào)迭代,比較不推薦遞歸形式,提供的功能與特色多半屬于迭代,若可能的話,建議盡量以迭代形式實(shí)現(xiàn)程序。但是,有些問題本質(zhì)上就屬于遞歸,較難以迭代形式表達(dá),例如深度與形狀不定的數(shù)據(jù)結(jié)構(gòu)、文件系統(tǒng)的目錄結(jié)構(gòu)、快速排序法等。7.5
遞歸我們能以遞歸的形式來使用函數(shù),意思是說函數(shù)可以以直接或間接的方式自己調(diào)用自己。以階乘為例,給定一整數(shù)n,階乘的數(shù)學(xué)記號(hào)為“n!”,若根據(jù)下面的定義,很直覺地會(huì)寫出迭代形式的程序:n!=1*2*3*...*(n-1)*n0!=1但若定義如下,那么便會(huì)得到遞歸形式:n!=n*(n-1)! 如果n大于或等于2n!=1 如果n等于1或0【程序代碼7-4】階乘。#fact.pydeffact_i(n):#迭代形式
result=1foriinrange(1,n+1):result*=ireturnresult####deffact_r(n):#遞歸形式
ifn==1orn==0:return1else:returnn*fact_r(n-1)7.5
遞歸7.5
遞歸函數(shù)fact_r運(yùn)用遞歸的概念來計(jì)算n!。根據(jù)定義,如果n大于或等于2,就再次調(diào)用fact_r,但傳入(n-1)計(jì)算(n-1)!,得到返回值后再乘上n,便得到n!。只要每次調(diào)用fact_r時(shí),都傳入較小的數(shù)值,那么最終n會(huì)等于1或0,就不會(huì)再遞歸調(diào)用、而是直接返回1。7.5.1遞歸的概念以遞歸方式編寫函數(shù)時(shí),基本概念如下:(1)每次遞歸調(diào)用,都必須趨近終止條件。(2)抵達(dá)終止條件時(shí),就不再遞歸調(diào)用。以階乘函數(shù)fact_r為例,fact_r(n)遞歸調(diào)用fact_r(n-1),n會(huì)越來越小,最終抵達(dá)終止條件,也就是當(dāng)n等于1或0時(shí),便不再遞歸調(diào)用?!境绦?qū)嵗?-5】計(jì)算兩數(shù)的最大公因數(shù),練習(xí)以遞歸形式實(shí)現(xiàn)函數(shù)。#gcd.pydefgcd_i(a,b): #迭代形式
whileb:a,b=b,a%breturna####defgcd_r(a,b): #遞歸形式
ifb==0: #終止條件
returnaelse:returngcd_r(b,a%b) #遞歸調(diào)用7.5.1遞歸的概念7.5.2實(shí)例:漢諾塔漢諾塔(又稱河內(nèi)塔)是著名的遞歸問題。有三根柱子,其中一根柱子從下到上按照大小順序疊了64個(gè)圓盤,要求把所有圓盤依原來順序搬移到另一根柱子。搬移圓盤時(shí)的規(guī)則是:(1)一次只能搬移一個(gè)圓盤。(2)圓盤必須從某柱子移動(dòng)到另一根柱子,不可放在別的地方。(3)小圓盤必須位于大圓盤之上;換句話說,大圓盤不可疊在小圓盤之上。若搬移一次花費(fèi)一秒鐘,那么最少需花多少時(shí)間才能搬完64個(gè)圓盤?7.5.2實(shí)例:漢諾塔我們先試試3個(gè)圓盤的情況,如圖,把三根柱子標(biāo)示為A、B、C,假設(shè)想從A搬移到C,圓盤從下到上編號(hào):0、1、2。那么,搬移的步驟如下:2從A搬到C、1從A搬到B、2從C搬到B、0從A搬到C、2從B搬到A、1從B搬到C、2從A搬到C。真啰嗦,其實(shí)因?yàn)榘嵋茣r(shí)必須遵守既定規(guī)則,所以直接表示為下面的形式即可,無須標(biāo)示搬移哪個(gè)圓盤:
三個(gè)圓盤的漢諾塔7.5.2實(shí)例:漢諾塔A->C、A->B、C->B、A->C、B->A、B->C、A->C3個(gè)圓盤需要移動(dòng)7次,需花費(fèi)7秒。那么4
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度網(wǎng)絡(luò)安全防護(hù)系統(tǒng)建設(shè)公司正規(guī)合同3篇
- 二零二五年度公司對(duì)公司展覽展示空間租賃合同3篇
- 2025年度生物科技企業(yè)職工招聘與生物多樣性保護(hù)合同3篇
- 二零二五年度礦產(chǎn)資源開發(fā)承包合同3篇
- 養(yǎng)老院院民2025年度社區(qū)活動(dòng)出行安全協(xié)議3篇
- 2025年度建筑材料供貨與建筑節(jié)能改造合同3篇
- 二零二五年度全屋衣柜定制及安裝一體化合同3篇
- 二零二五年度文化創(chuàng)意產(chǎn)業(yè)合伙合同協(xié)議3篇
- 2025年度企業(yè)合規(guī)管理委托代理合同3篇
- 2025年度全新出售房屋買賣智能家居集成協(xié)議3篇
- DB42-T 2219-2024 建筑施工企業(yè)從業(yè)人員安全培訓(xùn)標(biāo)準(zhǔn)
- 福建省龍巖市2023-2024學(xué)年高一1月期末生物試題
- 養(yǎng)老集市活動(dòng)方案
- GB/T 18336.5-2024網(wǎng)絡(luò)安全技術(shù)信息技術(shù)安全評(píng)估準(zhǔn)則第5部分:預(yù)定義的安全要求包
- 足療技師規(guī)章制度
- 指導(dǎo)農(nóng)戶科學(xué)種植工作總結(jié)報(bào)告
- 2024年江蘇省南京市公共工程建設(shè)中心招聘高層次專業(yè)技術(shù)人才1人歷年高頻考題難、易錯(cuò)點(diǎn)模擬試題(共500題)附帶答案詳解
- 2020年10月自考00094外貿(mào)函電試題及答案含解析
- 中等職業(yè)學(xué)校教研教改工作總結(jié)
- 甲狀腺細(xì)針穿刺細(xì)胞學(xué)檢查課件
- 醫(yī)療廢物管理人員及相關(guān)工作人員培訓(xùn)制度(15篇范文)
評(píng)論
0/150
提交評(píng)論