python程序設(shè)計(jì) 課件 董付國 第7-12章 函數(shù)、模塊 -程序設(shè)計(jì)案例分析_第1頁
python程序設(shè)計(jì) 課件 董付國 第7-12章 函數(shù)、模塊 -程序設(shè)計(jì)案例分析_第2頁
python程序設(shè)計(jì) 課件 董付國 第7-12章 函數(shù)、模塊 -程序設(shè)計(jì)案例分析_第3頁
python程序設(shè)計(jì) 課件 董付國 第7-12章 函數(shù)、模塊 -程序設(shè)計(jì)案例分析_第4頁
python程序設(shè)計(jì) 課件 董付國 第7-12章 函數(shù)、模塊 -程序設(shè)計(jì)案例分析_第5頁
已閱讀5頁,還剩497頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第7章函數(shù)Python程序設(shè)計(jì)第7章函數(shù)計(jì)算機(jī)科學(xué)家尼克勞斯·沃思是好幾種程序語言(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í)行一次,例如算出列表元素(成績)的總和,若手上有好幾個(gè)列表,想要讓同一個(gè)程序代碼應(yīng)用于所有這些列表呢?我們需要一種抽象化機(jī)制來包裝程序代碼,在有需要時(shí)可以隨時(shí)調(diào)用它,這個(gè)機(jī)制叫做函數(shù)(function)。定義函數(shù)的語法大致如下:def<函數(shù)名>(<形參)):<函數(shù)體(body)><函數(shù)名>是標(biāo)識(shí)符,<形參>即形式參數(shù),是名稱(標(biāo)識(shí)符)的序列(可能為空)。形參與函數(shù)中使用的所有變量一樣,只能在函數(shù)體中訪問,它與在程序其他地方的同名變量不同。7.1

函數(shù)的定義def語句的作用是定義函數(shù),當(dāng)Python解釋器執(zhí)行def語句時(shí),會(huì)建立類型為function(函數(shù))的對(duì)象,并指定給名稱。使用保留字def開頭,后面接著函數(shù)名稱,然后以括號(hào)包住參數(shù),加上冒號(hào)后,便開始縮排并編寫函數(shù)的主體。從“def”到“:”叫做函數(shù)頭,而里面的程序代碼稱為函數(shù)體,函數(shù)結(jié)束時(shí)可使用“return語句”將結(jié)果返回給調(diào)用方?!境绦?qū)嵗?-1】傳入含有成績的列表,算出成績總分之后返回的函數(shù)。defmy_sum(numbers): #函數(shù)頭,函數(shù)名稱與參數(shù)

total=0 #負(fù)責(zé)存儲(chǔ)總分,這是個(gè)“局部”名稱

forxinnumbers: #以for循環(huán)算總分

total+=xreturntotal #以return語句返回總分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ì)象長度的函數(shù):defmy_len(seq): #seq可以是列表、數(shù)組、字符串

n=0forxinseq:n+=1 #有幾個(gè)元素便加幾次1returnn #返回長度函數(shù)定義與函數(shù)調(diào)用是兩個(gè)獨(dú)立的概念,分別代表不同的動(dòng)作,如果只執(zhí)行def語句,僅會(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)庫的函數(shù)和方法(如math.sqrt)。函數(shù)是構(gòu)建復(fù)雜程序的重要工具。7.1

函數(shù)的定義可以將函數(shù)想象成是一個(gè)“子程序”,其基本思想是寫一個(gè)語句序列,并給這個(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è)簡單方法是使用四個(gè)print語句。下面的交互式會(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打印一行歌詞?,F(xiàn)在我們用happy來為xiaoming重寫歌詞。>>>defsingXM():happy()happy()print("Happybirthday,dearxiaoming.")happy()這個(gè)版本打的字要少得多。試運(yùn)行這個(gè)程序。7.1

函數(shù)的定義假設(shè)今天也是小芳(xiaofang)的生日,我們同樣也可以為小芳準(zhǔn)備一個(gè)程序(singXF())。現(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語句結(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ī)制,可以縮短和簡化程序。在實(shí)踐中,即使函數(shù)實(shí)際上讓程序更長,人們也會(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í)行了下面的語句: person="xiaoming"在這里,Python開始執(zhí)行sing的函數(shù)體。第一個(gè)語句是happy函數(shù)調(diào)用,Python暫停執(zhí)行sing并將控制傳遞給被調(diào)用的函數(shù)。happy的函數(shù)體包含一個(gè)print。這個(gè)語句被執(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í)行的語句是main中的不帶參數(shù)print語句,這將在輸出中形成空行。然后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)“*”用法的一般語法是: 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é)問很大,語法很復(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庫調(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ā)生在賦值語句的右側(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語句組成。當(dāng)Python遇到return時(shí),它立即退出當(dāng)前函數(shù),并將控制返回到函數(shù)被調(diào)用之后的點(diǎn)。此外,return語句中提供的值作為表達(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語句的函數(shù)。我們可以不讓輔助函數(shù)執(zhí)行打印,而是簡單地讓它們返回值(在這個(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ù)返回值的功能精簡了程序,讓整個(gè)句子建立在單個(gè)字符串表達(dá)式中。請(qǐng)仔細(xì)查看并理解這行代碼。這個(gè)版本的程序比原來的更靈活,因?yàn)榇蛴〔辉俜植荚诙鄠€(gè)函數(shù)中。例如,我們可以輕松地修改程序,將結(jié)果寫入文件而不是屏幕。我們要做的是打開一個(gè)文件進(jìn)行寫入,并在print語句中添加一個(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語句中簡單地列出多個(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語句。沒有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語句把執(zhí)行結(jié)果(對(duì)象)返回給調(diào)用方;一個(gè)函數(shù)內(nèi)的return語句可以有多個(gè)。若return后面沒有東西、或者根本沒有return語句,那么函數(shù)結(jié)束時(shí)會(huì)返回None。雖然return只能返回一個(gè)對(duì)象,但可返回tuple對(duì)象,所以可以把許多個(gè)對(duì)象放在tuple里,然后返回。7.3

函數(shù)的返回值例如,之前算總分與求平均成績的程序代碼,可寫成如下的函數(shù):deftotal_avg(scores,initial=0):n=0total=initialforxinscores: #以for循環(huán)計(jì)算總分

total+=xn+=1 #也順便得知長度

return(total,total/n) #返回含有總分與平均成績的數(shù)組建立數(shù)組的括號(hào)“()”通??墒÷?,所以可改寫為“returntotal,total/n”。7.4函數(shù)的生存周期與作用域第一次指定名稱同名問題del語句7.4

函數(shù)的生存周期與作用域前面學(xué)習(xí)的程序代碼一旦產(chǎn)生某名稱后,就一直使用、直到程序結(jié)束。但“del語句”可以刪除名稱,連帶刪除該名稱與指向?qū)ο笾g的綁定關(guān)系。>>>a=3 #名稱a指向int對(duì)象3>>>a+=1 #名稱a轉(zhuǎn)而指向新的int對(duì)象4,3變成垃圾>>>dela #使用del語句刪除名稱a,也刪除與對(duì)象之間的綁定關(guān)系7.4

函數(shù)的生存周期與作用域“對(duì)象”的作用域(又稱可視范圍)實(shí)際上是“名稱”的作用域,決定了在程序的什么地方能夠看到該名稱,進(jìn)而存取該名稱指向的對(duì)象;這取決于該名稱定義的地方(第一次賦值)。如果名稱定義在程序文件最外層的地方,例如下面例子中的a、b、foo、x、y,一旦定義后,任何地方都看得到這些名稱,也能存取名稱指向的對(duì)象,簡稱“全局變量”;而定義在函數(shù)內(nèi)的名稱(包括參數(shù)),如下面的n與m,只擁有“局部”作用域,只有在該函數(shù)內(nèi)才看得到這些名稱,簡稱“局部變量”。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ù)里把某全局變量放在賦值語句的左邊,就會(huì)產(chǎn)生局部變量,而不是把新對(duì)象賦值給全局變量。為了把新對(duì)象賦值給全局變量,辦法是使用“global語句”。a=10 #全局變量adeffoo(n):globala #以global語句宣告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語句,名稱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=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語句聲明位于全局范圍,所以,也能在函數(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語句顧名思義,del語句的作用就是刪除名稱,連帶地也會(huì)刪除名稱與對(duì)象之間的綁定關(guān)系,若對(duì)象已無名稱指向它,便成為垃圾等待回收。del語句在全局范圍內(nèi)會(huì)刪除全局名稱,而在局部范圍內(nèi)會(huì)刪除局部名稱。y=999dely #刪除全局名稱yx=3deffoo():globalx #聲明x為全局名稱

delx #刪除foo()print(x) #錯(cuò)誤,找不到x7.4.3del語句提醒一下,若在函數(shù)內(nèi)有del語句,且名稱未以global聲明,該名稱將會(huì)被視為區(qū)域名稱,連帶影響其他的程序語句。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語言強(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個(gè)圓盤呢?若以迭代方式思考,很難想象,但若以遞歸方式思考,當(dāng)想要把4個(gè)圓盤從A搬到C時(shí),步驟是:把上三個(gè)圓盤從A搬到B,把最下面第四個(gè)圓盤從A搬到C,再把上三個(gè)圓盤從B搬到C,如此即可。所以搬4個(gè)圓盤的次數(shù)是“搬3個(gè)圓盤的次數(shù)”+“搬1個(gè)圓盤的次數(shù)”+“搬3個(gè)圓盤的次數(shù)”,7+1+7等于15。7.5.2實(shí)例:漢諾塔分析如下:(1)搬1個(gè)圓盤的次數(shù):0+1+0等于2**1-1等于1(2)搬2個(gè)圓盤的次數(shù):1+1+1等于2**2-1等于3(3)搬3個(gè)圓盤的次數(shù):3+1+3等于2**3-1等于7(4)搬4個(gè)圓盤的次數(shù):7+1+7等于2**4-1等于15(5)搬n個(gè)圓盤的次數(shù):2**(n-1)-1+1+2**(n-1)-1等于2**n–1(6)搬64個(gè)圓盤的次數(shù):2**63-1+1+2**63-1等于2**64–1所以,若搬一次需花一秒鐘,那么想搬移64個(gè)圓盤需花費(fèi)大約5849.42億年!7.5.2實(shí)例:漢諾塔下面讓我們編寫函數(shù)hanoi(n),參數(shù)是n代表幾個(gè)圓盤,返回含有搬移步驟的列表,例如調(diào)用hanoi(3)搬移3個(gè)圓盤的步驟會(huì)是[('A','C'),('A','B'),('C','B'),('A','C'),('B','A'),('B','C'),('A','C')],每個(gè)元素是個(gè)tuple、代表一個(gè)步驟,如('A','C')代表從柱子A、把最上面的圓盤、搬到柱子C?!境绦?qū)嵗?-6】漢諾塔。#Hanoi.pydefhanoi(n):steps=[] #含有搬移步驟的列表

#pfrom是起始柱子,pto是目標(biāo)柱子,pbuf是緩沖柱子

defsub(n,pfrom,pto,pbuf):ifn==1: #終止條件

steps.append((pfrom,pto)) #從pfrom搬到pto,搬1個(gè)

else: #遞歸調(diào)用

sub(n-1,pfrom,pbuf,pto) #從pfrom搬到pbuf,搬n-1個(gè)

steps.append((pfrom,pto)) #從pfrom搬到pto,搬1個(gè)

sub(n-1,pbuf,pto,pfrom) #從pbuf搬到pto,搬n-1個(gè)

sub(n,'A','C','B')returnsteps7.5.2實(shí)例:漢諾塔有了搬移步驟后,我們還可以編寫測(cè)試函數(shù),照著步驟進(jìn)行搬移,檢查是否正確。defsimulate_hanoi(n,pfrom,pto,pbuf,steps):#一開始時(shí),pfrom里有n個(gè)圓盤

pillars={pfrom:list(range(n)),pto:[],pbuf:[]}forsinsteps: #照著步驟進(jìn)行搬移

disk=pillars[s[0]].pop() #拿出圓盤

pillars[s[1]].append(disk) #放進(jìn)柱子

if(pillars[pfrom]==[]andpillars[pbuf]==[]andpillars[pto]==list(range(n))): #最后圓盤應(yīng)該都搬到pto里

returnTrueelse:returnFalse7.5.2實(shí)例:漢諾塔7.5.2實(shí)例:漢諾塔其中字典pillars,鍵是代表柱子的pfrom、pto、pbuf,值則是含有圓盤的列表,以for循環(huán)迭代搬移步驟steps,按照步驟搬移,每個(gè)步驟會(huì)從某列表(含有圓盤)的尾端拿出圓盤、放進(jìn)別的列表的尾端,剛好能以列表方法pop與append代表。搬移結(jié)束后,檢查各柱子里是否含有適當(dāng)?shù)膱A盤。謝謝大家第8章模塊Python程序設(shè)計(jì)第9章模塊當(dāng)軟件項(xiàng)目越來越大,軟件越來越復(fù)雜時(shí),若是由團(tuán)隊(duì)來開發(fā),就不能把所有程序代碼都放在單一源文件里,而需要透過某種規(guī)則與機(jī)制,分散到許多文件中,這時(shí),自然會(huì)產(chǎn)生許多需求,借以管理軟件程序代碼、劃分權(quán)責(zé)、便于重復(fù)使用。實(shí)際上,每個(gè)程序語言都有一套機(jī)制來管理程序,諸如函數(shù)庫、程序庫、模塊、包、類庫、軟件開發(fā)框架等等,目的就是程序代碼重復(fù)使用。第9章模塊在Python中,這樣的機(jī)制叫模塊(module)與包(package,又稱套件),模塊被用來組織程序的架構(gòu)。目前,各種與包相關(guān)的文件、工具、標(biāo)準(zhǔn)制定,由Python包委員會(huì)(PyPA)團(tuán)體負(fù)責(zé),包括Python包使用者手冊(cè)等,有興趣深入了解的讀者,可以瀏覽PyPA的網(wǎng)站。第9章模塊模塊機(jī)制將程序代碼依照功能分離,分別放在不同的模塊文件里,避免名稱沖突,但這還不夠,因?yàn)橹敖榻B的模塊,其中的名稱所指向的對(duì)象都是函數(shù)、類、整數(shù)、列表等,而有時(shí)情況更為復(fù)雜,需要擁有模塊嵌套機(jī)制,這種能夠“含有子模塊的模塊”就是包。與過去不同,現(xiàn)在學(xué)習(xí)程序設(shè)計(jì)時(shí),通常需要集中大部分精力在程序庫上,熟悉其框架與用法,根據(jù)你想要開發(fā)哪個(gè)領(lǐng)域的程序軟件,挑選適合的程序庫或開發(fā)框架。模塊的概念1讀入模塊:import與from4模塊的使用2使用內(nèi)置模塊3第三方模塊5目錄8.1模塊的概念模塊:獨(dú)立的.py文件包:按目錄組織模塊8.1

模塊的概念程序語言不會(huì)單獨(dú)存在,而有豐富的程序庫對(duì)其進(jìn)行擴(kuò)展和支撐,例如數(shù)學(xué)運(yùn)算、網(wǎng)絡(luò)連接、3D繪圖、音頻處理、機(jī)器人控制等等。如果沒有適當(dāng)?shù)某绦驇?,在開發(fā)時(shí)我們就要辛苦地編寫底層程序,而不能專注于真正想要開發(fā)的上層軟件功能。當(dāng)Python程序日漸龐大時(shí),需要將程序代碼根據(jù)功能特色適當(dāng)分割,供不同領(lǐng)域的開發(fā)者選擇與使用。Python的模塊可以是Python,也可以是C(或其他)語言的程序代碼。8.1.1模塊:獨(dú)立的.py文件在程序開發(fā)過程中,為了編寫可維護(hù)的代碼,開發(fā)人員會(huì)把函數(shù)分組,分別放到不同的文件里,這樣,每個(gè)文件包含的代碼就相對(duì)較少。在Python中,一個(gè)獨(dú)立的.py文件就稱為一個(gè)模塊。模塊編寫完畢后,可以在其他程序代碼段中引用(程序復(fù)用)。我們?cè)诰帉懗绦虻臅r(shí)候可以引用Python內(nèi)置的模塊,自定義的模塊和來自第三方開發(fā)者的模塊。使用模塊還可以避免函數(shù)名和變量名的沖突。相同名字的函數(shù)和變量可以分別存在于不同的模塊中,因此,編寫模塊時(shí)不必考慮名字會(huì)與其他模塊沖突。但是,模塊名要遵循Python變量命名規(guī)范,注意名字不要與內(nèi)置函數(shù)名字沖突。8.1.2包:按目錄組織模塊一些較大規(guī)模的程序設(shè)計(jì)工作通常是團(tuán)隊(duì)合作的成果。為了避免合作中可能造成的模塊名沖突,Python又引入了按目錄來組織模塊的方法,稱為包(Package)。例如,abc.py文件就是一個(gè)名字叫abc的模塊,xyz.py文件就是一個(gè)名字叫xyz的模塊。現(xiàn)在假設(shè)abc和xyz這兩個(gè)模塊名字與其他模塊沖突了,于是,可以通過包來組織模塊,避免沖突。方法是選擇一個(gè)頂層包名,比如mycompany,按照目錄存放。目錄結(jié)構(gòu)(1)8.1.2包:按目錄組織模塊引入包以后,只要頂層包的名字不與別人沖突,那所有模塊都不會(huì)與別人沖突?,F(xiàn)在,abc.py模塊的名字就變成了mycompany.abc,類似的,xyz.py的模塊名變成了mycompany.xyz。每一個(gè)包目錄下面都必須存在一個(gè)__init__.py文件,否則Python會(huì)把這個(gè)目錄當(dāng)成普通目錄。__init__.py可以是空文件,也可以有Python代碼,因?yàn)開_init__.py本身就是一個(gè)模塊,而它的模塊名就是mycompany。類似的,可以有多級(jí)目錄,組成多級(jí)層次的包結(jié)構(gòu)。文件www.py的模塊名就是mycompany.web.www,兩個(gè)文件utils.py的模塊名分別是mycompany.utils和mycompany.web.utils。目錄結(jié)構(gòu)(2)8.1.2包:按目錄組織模塊自己創(chuàng)建模塊時(shí),要注意命名不能和Python自帶的模塊名稱沖突。例如,系統(tǒng)自帶了sys模塊,自己的模塊就不可命名為sys.py,否則將無法導(dǎo)入系統(tǒng)自帶的sys模塊。模塊的讀入自定義程序模塊標(biāo)準(zhǔn)程序庫順序搜索模塊8.2模塊的使用8.2

模塊的使用一個(gè)Python程序可以由一個(gè)或多個(gè)模塊組成,不管是語句(def、for、賦值)或表達(dá)式(運(yùn)算符、函數(shù)調(diào)用、列表生成式),都必須放在模塊里。Python解釋器執(zhí)行時(shí),也是以模塊為執(zhí)行單位,當(dāng)想使用別的模塊文件里定義的函數(shù)、類型、常數(shù)時(shí),須以語句import來讀入,基本語法如下: import模塊名8.2.1模塊的讀入下面以兩個(gè)文件為例,一個(gè)是主程序文件,一個(gè)是模塊文件,主程序文件會(huì)讀入模塊文件,使用里面定義的對(duì)象?!境绦?qū)嵗?-1】主程序文件。#myhello.pya=26b=16importmymath #讀入模塊defmain():print('HelloPython')print('piis'+str(mymath.pi)) #使用

print('gcd(%d,%d)is%d'%(a,b,mymath.gcd(a,b)))8.2.1模塊的讀入print('factorial(%d)is%d'%(6,mymath.factorial(6)))print('ByePython')if__name__=='__main__':print('%sasmainprogram'%__name__)print('mymath.__name__is%s'%mymath.__name__)main()8.2.1模塊的讀入【程序?qū)嵗?-2】模塊文件。#mymath.pypi=3.14defgcd(a,b):whileb:a,b=b,a%breturnadeffactorial(n):result=18.2.1模塊的讀入foriinrange(1,n+1):result*=ireturnresultif__name__=='__main__':print('%sasmainprogram'%__name__)8.2.1模塊的讀入8.2.1模塊的讀入不管是哪一個(gè)文件,對(duì)Python解釋器來說都是模塊。主程序文件是整個(gè)程序的入口,所以也稱為主模塊文件。每個(gè)模塊都有個(gè)名為“__name__”的屬性項(xiàng),存放著代表模塊名的字符串,主模塊的__name__會(huì)是'__main__',而其他模塊的__name__是該模塊文件的主文件名。運(yùn)行結(jié)果:在命令行下達(dá)指令“pythonmyhello.py”。$pythonmyhello.py__main__asmainprogrammymath.__name__ismymathHelloPythonpiis3.14gcd(26,16)is2factorial(6)is720ByePython8.2.1模塊的讀入程序分析:命令中的myhello.py是主程序文件,Python解釋器為它建立模塊(也是對(duì)象),其命名空間成為全局命名空間,串聯(lián)的內(nèi)置命名空間成為當(dāng)前環(huán)境,在此環(huán)境里執(zhí)行語句,所以之后的賦值語句與def語句,會(huì)在當(dāng)前環(huán)境里產(chǎn)生名稱a、b、main并指向相對(duì)應(yīng)的對(duì)象。執(zhí)行語句“importmymath”時(shí),解釋器先尋找名為mymath的模塊文件(擴(kuò)展名不一定是.py),找到后載入并初始化,同樣建立模塊對(duì)象,此模塊的命名空間成為mymath.py程序代碼的全局命名空間,在此環(huán)境里面執(zhí)行mymath.py的語句,所以在全局范圍里建立的名稱pi、gcd、factorial被放進(jìn)全局命名空間里,也就是該模塊的命名空間;該模塊的__name__是'mymath'、不是'__main__',所以mymath.py的if語句為假、不會(huì)執(zhí)行。8.2.1模塊的讀入成功載入模塊后,回到myhello.py的語句“importmymath”,此語句因處于全局范圍,所以在全局命名空間里產(chǎn)生名稱mymath,指向剛剛載入的模塊對(duì)象。然后,因?yàn)橹髂K的__name__是'__main__',所以if語句為真,進(jìn)入執(zhí)行,執(zhí)行print輸出一些信息,接著調(diào)用函數(shù)main,通過名稱mymath(指向模塊對(duì)象)存取該模塊的屬性項(xiàng),也就是該模塊命名空間里的各個(gè)名稱與其指向的對(duì)象,于是便能調(diào)用mymath.pi指向的int對(duì)象,調(diào)用mymath.gcd與mymath.factorial指向的函數(shù)對(duì)象。8.2.1模塊的讀入若下達(dá)指令“pythonmymath.py”的話,流程同上,Python解釋器把mymath.py當(dāng)作主程序文件建立模塊對(duì)象,并把它的屬性項(xiàng)__name__設(shè)為'__main__',它的命名空間成為全局命名空間,在此環(huán)境下執(zhí)行程序語句,建立對(duì)象并指派給名稱pi、gcd、factorial,然后執(zhí)行if語句為真,輸出一些信息后結(jié)束。$pythonmymath.py__main__asmainprogram8.2.2自定義程序模塊下面這個(gè)簡單例子有兩個(gè)文件,一個(gè)是主程序文件,一個(gè)是模塊文件,把這兩個(gè)文件放在同一個(gè)目錄里。模塊文件(mymath.py)里有浮點(diǎn)數(shù)pi、函數(shù)gcd(計(jì)算兩個(gè)整數(shù)的最大公因數(shù))、函數(shù)factorial(階乘)?!境绦?qū)嵗?-3】模塊文件。#mymath.pypi=3.14defgcd(a,b): #最大公約數(shù)

whileb:a,b=b,a%breturnadeffactorial(n): #n階乘等于1*2*3*...*nresult=1foriinrange(1,n+1):result*=ireturnresult8.2.2自定義程序模塊8.2.2自定義程序模塊在主程序文件(myhello.py)里使用“importmymath”讀入模塊,不要加擴(kuò)展名.py。讀入后,名稱mymath指向剛剛讀入的模塊。模塊在Python里也是個(gè)對(duì)象,類型是module。【程序?qū)嵗?-4】主程序文件。#myhello.pyimportmymathprint('---HelloPython---')print('piis'+str(mymath.pi))print('gcdof24and16is'+str(mymath.gcd(24,16)))print('factorialof6is'+str(mymath.factorial(6)))print('---ByePython---')8.2.2自定義程序模塊8.2.2自定義程序模塊程序分析:在myhello.py里使用“importmymath”的作用就如執(zhí)行mymath.py一樣,只不過執(zhí)行后得到的名稱,會(huì)被放入模塊對(duì)象里,因?yàn)镻ython模塊對(duì)象有命名空間的功能,也就是說可存放東西(名稱);讀入后,可以使用“模塊名.名稱”的語法存取模塊里的名稱(及其所指向的對(duì)象)。8.2.3標(biāo)準(zhǔn)程序庫除了Python語言的實(shí)例,Python的模塊也可以使用其他語言來開發(fā),例如CPython的標(biāo)準(zhǔn)程序庫模塊就采用C語言實(shí)例。在模塊keyword里,列表kwlist含有Python的保留字,而函數(shù)iskeyword可判定某字符串是否為保留字。>>>importkeyword #讀入模塊keyword>>>keyword.kwlist #含有所有保留字的列表['False','None','True','and','as','assert','break','class','continue','def','del','elif','else','except','finally','for','from','global','if','import','in','is','lambda','nonlocal','not','or','pass','raise','return','try','while','with','yield']>>>len(keyword.kwlist) #有33個(gè)保留字33>>>keyword.iskeyword('import') #import是保留字True>>>keyword.iskeyword('a') #a不是保留字False8.2.3標(biāo)準(zhǔn)程序庫8.2.3標(biāo)準(zhǔn)程序庫有個(gè)模塊含有Python全部的內(nèi)置名稱,包括函數(shù)、常數(shù)、異常等,而且已缺省讀入,名稱是__builtins__,調(diào)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論