第八章 函數(shù)-1
到現(xiàn)在為止,我們學(xué)完了Python的基礎(chǔ)知識(shí)和程序的三種控制結(jié)構(gòu),函數(shù)這一章是幫助我們向更高的程序設(shè)計(jì)階段進(jìn)發(fā),一般說來,所有的程序設(shè)計(jì)語言里都有函數(shù)(有的語言稱為模塊),這是為什么呢?
想想第三章我們介紹了內(nèi)置函數(shù),為啥要有內(nèi)置函數(shù)呢?因?yàn)檫@些功能經(jīng)常要用到,所以編寫成一個(gè)函數(shù)供大家使用。但是這些函數(shù)都是通用功能,比如數(shù)學(xué)函數(shù)、字符串函數(shù)等等。不同領(lǐng)域常用功能不同,所以不同專業(yè)需要自己的函數(shù),我們編寫完函數(shù)之后還可以給其他人用,這就是代碼復(fù)用。其實(shí)我們自己編寫程序也應(yīng)該是把寫過的代碼保存好,萬一以后用到呢?夢(mèng)想還是要有的,萬一實(shí)現(xiàn)了呢。
8.1 函數(shù)定義
函數(shù)是一種僅在調(diào)用時(shí)運(yùn)行的代碼塊??梢詫?shù)據(jù)(稱為參數(shù))傳遞到函數(shù)中,函數(shù)可以把數(shù)據(jù)作為結(jié)果返回。具體的語法如下:
def 函數(shù)名([參數(shù)列表]):
#實(shí)現(xiàn)特定功能的多行代碼
[return [返回值]]
參數(shù)列表是用逗號(hào)分隔的標(biāo)識(shí)符,[ ]表示括起來的部分可以有也可以沒有,所以參數(shù)可以沒有,返回值可以沒有,不帶表達(dá)式的return相當(dāng)于返回 none。
我們先看一個(gè)例子,輸出一行星號(hào)。
def displayStar():
??? print("**********************")
這里函數(shù)名為displayStar,函數(shù)沒有參數(shù),沒有返回值。函數(shù)功能為打印一行星號(hào)的語句。
函數(shù)頭為def displayStar():
函數(shù)體為函數(shù)頭部下方有縮進(jìn)的部分,這里只有一條語句print("**********************")。
在PyCharm里輸入上述函數(shù)之后運(yùn)行程序,沒有任何輸出結(jié)果,因?yàn)閐isplayStar函數(shù)沒有被調(diào)用,所以不會(huì)執(zhí)行函數(shù)里的函數(shù)體代碼。
學(xué)過函數(shù)之后,我們需要學(xué)會(huì)定義一個(gè)main()函數(shù),在main函數(shù)中編寫代碼調(diào)用定義好的函數(shù),然后在程序中調(diào)用main函數(shù)。這樣程序執(zhí)行就會(huì)先執(zhí)行main函數(shù),如果main函數(shù)中調(diào)用了其它函數(shù),則程序轉(zhuǎn)向調(diào)用的函數(shù)執(zhí)行該函數(shù)的函數(shù)體,調(diào)用結(jié)束之后,返回到調(diào)用的位置繼續(xù)執(zhí)行下面的代碼。程序的執(zhí)行過程總結(jié)一下就是:當(dāng)程序調(diào)用一個(gè)函數(shù)時(shí),程序控制權(quán)就會(huì)轉(zhuǎn)移到被調(diào)用的函數(shù)上。當(dāng)執(zhí)行完函數(shù)的返回語句或執(zhí)行到函數(shù)結(jié)束時(shí),被調(diào)用函數(shù)就會(huì)將程序控制權(quán)交還給調(diào)用者。
為了執(zhí)行displayStar函數(shù),我們還需要編寫main函數(shù),在main函數(shù)中調(diào)用displayStar函數(shù),具體代碼如下:
def displayStar():
??? print("displayStar begin")
??? print("**********************")
??? print("displayStar end")
?
def main():
??? print("main begin")
??? displayStar()
??? print("main end")
?
main()
?
運(yùn)行程序結(jié)果為:
main begin
displayStar begin
**********************
displayStar end
main end
?
程序執(zhí)行過程如下圖所示:

根據(jù)圖示,結(jié)合程序運(yùn)行結(jié)果,大家可以分析一下程序的執(zhí)行過程。在這個(gè)例子的基礎(chǔ)上,我們?cè)倏匆粋€(gè)有參數(shù)的例子,改寫上面代碼如下:
def displayStar(n):
??? for i in range(n):
??????? print("**********************")
?
def main():
??? row=int(input("行數(shù):"))
??? displayStar(row)
?
main()
?
運(yùn)行程序結(jié)果為:
行數(shù):4
**********************
**********************
**********************
**********************
?
程序?qū)崿F(xiàn)了通過獲取輸入行值,輸出相應(yīng)行數(shù)的星號(hào)。
清楚了上面的例子,我們?cè)倏匆粋€(gè)函數(shù)例子,編程實(shí)現(xiàn)求指定區(qū)間的所有整數(shù)的和。如指定1-10,則計(jì)算出1+2+3+……+10的和。自己先試著編寫一下代碼然后再往下看吧。
?
哎呀,我不會(huì)寫呀!沒有關(guān)系,不用函數(shù)會(huì)做不?不就是計(jì)算累加和嗎,不難,如下:
s=0
x,y =eval(input("區(qū)間:"))
for i in range(x,y+1):
??? s=s+i
print(s)
?
好下面我們需要把它改成函數(shù)方式實(shí)現(xiàn)。⑴先定義函數(shù):def關(guān)鍵字,函數(shù)名,括號(hào)冒號(hào)都會(huì)寫
def n1n2Sum(n1,n2):? #函數(shù)名n1n2Sum求表示n1、n2之間的和。
函數(shù)頭搞定,下面看函數(shù)體。函數(shù)體不就是已知n1、n2,計(jì)算n1、n2之間所有整數(shù)的和。參考上面的編寫代碼如下
s=0??????????????? #這個(gè)千萬不要忘了
for i in range(n1,n2+1):
??? s=s+i
print(s)
所以函數(shù)定義完了,下面這樣,其實(shí)我就是從上復(fù)制過來的,但是要注意縮進(jìn):
def n1n2Sum(n1,n2):? #函數(shù)名表示n1、n2之間的和。
s=0??????????????? #這個(gè)千萬不要忘了
for i in range(n1,n2+1):
??? s=s+i
print(s)
接下來⑵定義main函數(shù),在main里調(diào)用上面的函數(shù)(當(dāng)然可以不定義main函數(shù),但是定義main函數(shù)程序的可讀性好,代碼規(guī)范)。函數(shù)調(diào)用的時(shí)候確保參數(shù)有確定值,所以需要通過input函數(shù)獲取用戶輸入。當(dāng)然可以直接指定一個(gè)值,如n1n2Sum(1,10),但是程序運(yùn)行就只能計(jì)算1-10的整數(shù)和,不靈活,不方便。
def main():
??? x, y = eval(input("區(qū)間:"))
??? n1n2Sum(x,y)?????? #調(diào)用函數(shù)n1n2Sum
⑶程序中調(diào)用main函數(shù),簡(jiǎn)單:main()
Over,如果不熟練的話,請(qǐng)?jiān)倬毩?xí)n遍,(n>=3),送你編程秘籍:無他,唯手熟爾!
我們?cè)倏纯磩偛诺拇a:
def n1n2Sum(n1,n2):
??? s=0
??? for i in range(n1,n2+1):
??????? s=s+i
??? print(s)
?
def main():
??? x, y = eval(input("區(qū)間:"))
??? n1n2Sum(x,y)
?
main()
?
假設(shè)我的要求改了(開發(fā)軟件過程中,用戶需求就是會(huì)變來變?nèi)サ?,所以用戶需求變更位于惹毛程序員的十件事前列),我想輸出的2倍,簡(jiǎn)單,函數(shù)n1n2Sum 的print(s)改成print(2*s)不就行啦。是的,可以但是代碼質(zhì)量不高,一般一個(gè)函數(shù)只完成一個(gè)功能,所以更好的方法是我們把函數(shù)定義成有返回值的,這樣保證函數(shù)的功能就是求指定區(qū)間所有整數(shù)的和。所以代碼改成下面這樣的:
def n1n2Sum(n1,n2):
??? s=0
??? for i in range(n1,n2+1):
??????? s=s+i
??? return s
?
def main():
??? x, y = eval(input("區(qū)間:"))
??? s=n1n2Sum(x,y)
??? print(2*s)
?
main()
?
我們看一下n1n2Sum(n1,n2)函數(shù)的功能,實(shí)現(xiàn)n1-n2之間所有整數(shù)的和??梢赃@樣理解,對(duì)于這個(gè)問題,已知兩個(gè)整數(shù)n1,n2,求這兩個(gè)數(shù)之間所有整數(shù)的和。已知一般可以作為函數(shù)參數(shù),求的結(jié)果可以作為返回值,函數(shù)體是具體的功能。所以使用函數(shù)編程時(shí),要考慮已知(作為函數(shù)的參數(shù))和求的結(jié)果(作為函數(shù)返回值)這兩部分,然后具體問題具體分析,再完成函數(shù)體部分。
我們?cè)賮硪粋€(gè)例子:求兩個(gè)整數(shù)的最大公約數(shù)和最小公倍數(shù),最大公約數(shù)使用數(shù)學(xué)家的算法,最小公倍數(shù)使用普通人的辦法。先看最大公約數(shù)的算法:
第 1 步:比較 A 和 B 這兩個(gè)數(shù),將 A 設(shè)置為較大的數(shù),B 為較小的數(shù)。
第 2 步:A 除以 B,得余數(shù) R(Remainder)。
第 3 步:如果 R=0,則最大公約數(shù)就是 B;否則將 B 賦值給 A,R賦值給 B,重復(fù)進(jìn)行前兩步。
按照上述算法,定義函數(shù)gcd,已知為 ???????????????,求的是 ????????????。所以gcd函數(shù)的參數(shù)為兩個(gè)整數(shù)a,b,返回值為a,b的最大公約數(shù)。函數(shù)體呢?參見上述算法描述呀!
第 1 步:比較 A 和 B 這兩個(gè)數(shù),將 A 設(shè)置為較大的數(shù),B 為較小的數(shù)。
對(duì)應(yīng)的Python表示為:???????????????????????????????????????
第 2 步:A 除以 B,得余數(shù) R(Remainder)。
對(duì)應(yīng)的Python表示為:???????????????????????????????????????
第 3 步:如果 R=0,則最大公約數(shù)就是 B;否則將 B 賦值給 A,R賦值給 B,重復(fù)進(jìn)行前兩步。
對(duì)應(yīng)的Python表示為:???????????????????????????????????????
實(shí)際上,最后一步需要認(rèn)真思考:①要使用循環(huán)實(shí)現(xiàn)而不是選擇操作,因?yàn)橛兄貜?fù)進(jìn)行 ②什么情況下執(zhí)行循環(huán),或者說循環(huán)條件怎么寫。否則應(yīng)該表達(dá)為R!=0。提示到此結(jié)束,編程吧,少年!
def gcd( a , b ):
??? #第 1 步:比較 A 和 B 這兩個(gè)數(shù),將 A 設(shè)置為較大的數(shù),B 為較小的數(shù)。
??? if a<b:
??????? a,b=b,a
??? #第2步:A 除以 B,得余數(shù) R(Remainder)。
??? r= a % b
??? #第3步:如果R=0,則最大公約數(shù)就是 B;否則將 B 賦值給 A,R賦值給 B,重復(fù)進(jìn)行前兩步。
??? while r != 0:
??????? a = b
??????? b = r
??????? r = a % b
??? return b
?
def main():
??? x, y = eval(input("x,y:"))
??? print(gcd(x,y))
?
main()
?
求最小公倍數(shù)就是普通人的思維方法。首先明確一點(diǎn),兩個(gè)數(shù)的最小公倍數(shù)值最大的可能是這兩個(gè)數(shù)的乘積(似乎有點(diǎn)繞,我居然沒有把自己繞進(jìn)去,厲害了哈),我們需要讓一個(gè)循環(huán)變量從這兩個(gè)數(shù)中的(大值,小值)開始向這兩個(gè)數(shù)的乘積方向依次加1試一下這個(gè)循環(huán)變量是否能夠整除這兩個(gè)數(shù)(自己思考并且圈一下前面括號(hào)里的答案),一直找到滿足能夠整除這兩個(gè)數(shù)的數(shù)為止(為止就是結(jié)束,結(jié)束在循環(huán)里就是break),這時(shí)這個(gè)數(shù)就是這兩個(gè)數(shù)的最小公倍數(shù),我需要回去推敲一下前面寫的對(duì)不對(duì)(大體思路是對(duì)的,修改了一下措辭)。實(shí)際上求最大公約數(shù)也可以使用類似的方法,因?yàn)槲覀兌际瞧胀ㄈ耍瑫簳r(shí)不能想出數(shù)學(xué)家提供的方法,反正我真想不出來。試著把上面求最小公倍數(shù)的方法修改一下,改成求最大公約數(shù)。還是先編程做完最小公倍數(shù),然后再考慮下一個(gè)最大公約數(shù)問題吧。好啦,編程去嘍!
?
def lcm( a , b ):
??? xb=a*b??? #思考一下這行的意義何在
??? if a<b:
??????? a,b=b,a
??? for i in range(a,a*b+1):
??????? if i % a == 0 and i % b == 0:
??????????? xb = i
??????????? break
??? return xb
?
def main():
??? x, y = eval(input("x,y:"))
??? print(lcm(x,y))
?
main()
?
#思考一下這行的意義何在
這個(gè)問題如果自己理解不了,上課時(shí)來問我吧!
接下來我們改上面的方法求最大公約數(shù)。
兩個(gè)數(shù)的最小公倍(此處改成? 大公約?? )數(shù)值最大(此處改成??????? )的可能是這兩個(gè)數(shù)的乘積(此處改成??????????? ),我們需要讓一個(gè)循環(huán)變量從這兩個(gè)數(shù)中的(大值,小值)開始向1方向依次減1試一下這兩個(gè)數(shù)是否能夠整除這個(gè)循環(huán)變量(自己思考并且圈一下前面括號(hào)里的答案),一直找到滿足能夠整除這個(gè)循環(huán)變量的數(shù)為止(為止就是結(jié)束,結(jié)束在循環(huán)里就是break),這時(shí)這個(gè)數(shù)就是這兩個(gè)數(shù)的最大公約數(shù)。好啦,編程去嘍!
def gcd( a , b ):
??? dy=1??? #思考一下這行的意義何在
??? if a<b:
??????? a,b=b,a
??? for i in range(b,0,-1):
??????? if a % i == 0 and b % i == 0:
??????????? dy = i
??????????? break
??? return dy
?
def main():
??? x, y = eval(input("x,y:"))
??? print(gcd(x,y))
?
main()
?
實(shí)際上,我還真有一個(gè)捷徑,悄悄告訴你:兩個(gè)數(shù)的乘積等于這兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)的乘積。這個(gè)有用嗎?當(dāng)然了,如果寫好了求最大公約數(shù)的函數(shù)gcd(x,y), 那么lcm(x,y)=x*y/ gcd(x,y)。