第12 章函式 - Python

文章推薦指數: 80 %
投票人數:10人

Python 程式的撰寫風格:. ✶ 函式前後均空2 行. ∗ 函式的參數. ▸ 函式可接受參數,並由呼叫程式來提供,例如:. ✶ 函式定義:. def printSum(x, y): print(f'兩個 ... 序言 Python簡介 執行Python程式 Python的資料 除錯 小烏龜繪圖模組 Python模組 字串 串列與字組 字典 決策結構 迴圈 函式 檔案 例外處理 物件與類別 第12章  函式 (1)函式 ∗函式(Function):具有名稱的一群指令 ▸函式的目的: ✶提供其他程式多次呼叫(Functioncall),以避免重複撰寫程式碼 →DRY(Don’trepeatyourself) ✶將相關的程式指令組在一起,讓整體程式更為模組化(Modularization),更符合我們了解問題解決的方式 #例如薪資系統功能可分為以下4個函式: *分成4個小函式,個別的問題較為獨立且單純,更易於解決 ▸函式的語法: def(): ✶def:函式定義(define),是複合指令,因此需以冒號結尾,且其本體指令群需縮排一層 ✶:函式的名稱(不可使用Python保留字,且需符合Python識別字格式的規定) ✶:函式的參數(0或多個) ✶:函式的本體指令群 ▸Python程式的撰寫風格: ✶函式前後均空2行 ∗函式的參數 ▸函式可接受參數,並由呼叫程式來提供,例如: ✶函式定義: defprintSum(x,y): print(f'兩個數值的和是:{x+y}') #x與y是函式所接收的參數,亦稱為「正式參數」(Formalparameter) ✶呼叫程式: a=30 b=50 printSum(a,b) #a與b是呼叫函式時所提供的參數,亦稱為「實際參數」(Actualparameter) ▸呼叫程式可給不同的參數來多次呼叫函式,以產生不同的結果,如此可避免重複撰寫類似的程式,讓程式效能更高,例如: a=30 b=50 printSum(a+3,b-5) printSum(10,20) printSum(a/3,b/5) 兩個數值的和是:78 兩個數值的和是:30 兩個數值的和是:20.0 ∗實際參數與正式參數的對應方式 ▸位置參數(Positionalparameter):以位置來相互對應,例如: ✶定義:deffun(a,b,c): ✶以位置對應呼叫:fun(1,2,3) →a=1,b=2,c=3 ▸名稱參數(Namedparameter)或鍵值參數(Keywordparameter):以名稱來相互對應,例如: ✶定義:deffun(a,b,c): ✶以名稱對應呼叫:fun(b=3,c=4,a=5) →a=5,b=3,c=4 ▸預設正式參數值,該參數可以在呼叫時省略,例如: ✶定義:deffun(a,b,c=5): ✶以位置對應呼叫,並省略預設參數:fun(2,3) →a=2,b=3,c=5 ✶無預設值之參數必須放在有預設值之參數之前,例如: deffun(a,b=4,c): →SyntaxError:non-defaultargumentfollowsdefaultargument ▸練習1 (2)小烏龜的畫正方形函式 ▸範例1:傳入顏色及尺寸,函式繪製正方形 ✶利用turtle模組來畫正方形時,我們希望寫相同一段程式來畫不同尺寸的正方形, 而不是針對每種尺寸的正方形都要寫一份程式(否則就要寫無限個程式了!) draw2Squares.py(首先建立python/ch12目錄) importturtle defdrawSquare(t,color,size): t.color(color)#小烏龜顏色 foriinrange(4): t.forward(size)#方形邊長 t.left(90)#左轉90度 screen=turtle.Screen() screen.setup(700,500) screen.bgcolor('lightyellow') myTurtle=turtle.Turtle() myTurtle.shape('blank')#小烏龜沒有形狀 drawSquare(myTurtle,'blue',100)#呼叫函式,給一組參數 myTurtle.left(180) myTurtle.penup() myTurtle.forward(100) myTurtle.pendown() drawSquare(myTurtle,'red',200)#呼叫函式,給另一組參數 screen.exitonclick() ✶drawSquare()函式: #其本體指令群縮排一層 #正式參數:t,color,step(小烏龜物件、筆色、及步數),由呼叫程式傳入 #t.color(color):設定小烏龜筆色 #foriinrange(4):總共執行4次之迴圈 *其本體指令縮排一層 *t.forward(size):往前走size距離 *t.left(90):左轉90度 ✶呼叫程式: #從screen=turtle.Screen()指令開始均無縮排,因此從這個指令以下都不屬於 drawSquare()函式的本體 #第一次呼叫drawSquare()函式,畫一個藍色、邊長為100的正方形 #小烏龜移到另一個位置 #第二次呼叫drawSquare()函式,畫一個紅色、邊長為200的正方形 ▸範例2:以旋轉方式畫20個尺寸漸大的彩色正方形 draw20Squares.py importturtle defdrawColorSquare(t,size): forcolorin['red','blue','orange','green']: t.color(color) t.forward(size) t.left(90) screen=turtle.Screen()#設定螢幕與特性 screen.setup(800,600) myTurtle=turtle.Turtle()#產生小烏龜 myTurtle.pensize(2) myTurtle.speed(10) size=20#最小正方形的尺寸 foriinrange(20): drawColorSquare(myTurtle,size) size+=10#每次增加方形尺寸 myTurtle.forward(10)#往前走10步 myTurtle.right(18)#右轉10度 screen.exitonclick() ✶drawColorSquare()函式:依照尺寸參數繪製彩色方形 ✶呼叫程式: #每次呼叫函式後均加長方形邊長且移動加旋轉 ▸練習2 (3)函式的回覆值與執行流程 ∗回覆值的函式:函式在執行完畢會回覆值給呼叫程式 ▸語法:在函式最後加上return指令,後面接一個或多個值或表示式 def(): ... return ∗函式的執行流程(Executionflow) ▸範例:計算數值的平方 square.py defsquare(x): y=x*x returny num=5 result=square(num) print(result) ▸程式的執行: ✶Python首先執行第1行指令:defsquare,Python知道是要定義一個函式, 並且登記函式名稱為square,如果以後遇到square,Python就會認得此名稱 #第2~3行是函式本體指令群,這些指令並不會執行,函式需要透過呼叫才會執行,因此執行流程跳過這些指令 ✶接著執行第6行指令:num=5 ✶接著執行第7行指令:result=square(num),此指令呼叫函式並將執行流程控制(Flowcontrol) 交給square()函式,從第1行開始執行 ✶接著執行第2,3行函式本體指令,return指令不僅回覆一個值, 同時也將流程控制交還給主程式,回到第7行指令將回覆值指派給result變數 ✶接著執行第8行指令,最後結束 ▸因此,程式執行的流程為行1→6→7→1→2→3→7→8 ▸程式定義要在呼叫程式之前,如此Python才會認得,因此以下程式會出現錯誤: square.py num=5 result=square(num) print(result) defsquare(x): y=x*x returny Traceback(mostrecentcalllast): File"square.py",line2,in result=square(num) NameError:name'square'isnotdefined →square名稱未定義,Python不認得 ▸如果一個函式沒有寫明回覆值,那麼預設回覆值就是None,例如: square.py defsquare(x): y=x*x #returny num=5 result=square(num) print(result) None (4)函式的參數與變數 ∗函式的參數與函式內的變數都是區域變數 ▸在上例的函式中,有參數x及變數y, 這兩者都只能在函式內部使用,在函式外面就無法使用,因此稱為區域變數(Localvariable),例如: square.py defsquare(x): y=x*x returny num=5 result=square(num) print(result) print(x) Traceback(mostrecentcalllast): File"square.py",line9,in print(y) NameError:name'x'isnotdefined →x名稱未定義,Python不認得(若將x改為 y後,同樣錯誤也會發生:Python也不認得) #註:在函式中,在指派指令左方使用變數名稱,Python就會產生一個區域變數 ▸結論: ✶區域變數只能在其所定義的區域中使用,在該區域以外就無法使用 ✶區域變數的壽命:函式開始執行時開始生命,函式執行完畢就結束 ∗全域變數(Globalvariable) ▸變數在所有地方都可使用 ▸例如: badSquare.py defsquare(x): y=x**power returny power=2 result=square(10) print(result) ✶power定義在主程式,屬全域變數,在函式中亦可使用 ▸注意:在函式裡使用全域變數是很不好的作法 (例如全域變數名稱修改,函式中的名稱也要修改),應該避免 ▸解決方案:在函式中絕對不要使用全域變數,應該以參數方式將值傳給函式,例如: goodSquare.py defsquare(x,power): y=x**power returny power=2 result=square(10,power) print(result) ▸當區域變數名稱與全域變數名稱相同,區域變數會遮蓋(Shadow)全域變數,例如: shadowSquare.py defsquare(x): power=4 y=x**power returny power=2 result=square(10) print(result) print(power) 10000 2 ✶在函式內的指派指令power=4會產生新的區域變數,不會使用全域變數 (全域變數遭到遮蓋),因此有兩個power變數同時存在 ✶最後一行指令會印出2,顯示全域變數power的值並未被修改 (5)使用主函式 ∗主函式(Mainfunction) ▸至目前為止,我們的程式包含主程式及函式 ▸將主程式轉為主函式是個不錯的作法,可以使我們的程式更模組化(Modularization) ▸例如繪製簡單方形的程式: drawSquare.py importturtle defdrawSquare(t,step): foriinrange(4): t.forward(step) t.left(90) screen=turtle.Screen() screen.setup(400,300) screen.bgcolor('lightyellow') myTurtle=turtle.Turtle() drawSquare(myTurtle,100) screen.exitonclick() ✶可以將主程式改寫為main()函式,並在最後一行呼叫main(): drawSquareMain.py importturtle defmain(): screen=turtle.Screen() screen.setup(400,300) screen.bgcolor('lightyellow') myTurtle=turtle.Turtle() drawSquare(myTurtle,100) screen.exitonclick() defdrawSquare(t,step): foriinrange(4): t.forward(step) t.left(90) main() ✶現在程式結構如下: 1.匯入turtle模組 2.定義主函式main() 3.定義drawSquare()函式 4.呼叫主函式 ✶最後才呼叫main()的效果:Python已讀取所有函式定義,因此 drawSquare()函式可以放在main()之後,Python會認得 ∗進一步模組化:漂亮的程式架構! ▸將程式分成模組,每個模組有其功能,例如以上程式可以規劃成五個模組:main(), createScreen(),createTurtle(),drawSquare(),exitScreen() drawSquareModule.py importturtle defmain(): screen=createScreen(400,300,'lightyellow') myTurtle=createTurtle() drawSquare(myTurtle,100) exitOnClick(screen) defcreateScreen(width,height,bgcolor): screen=turtle.Screen() screen.setup(width,height) screen.bgcolor(bgcolor) returnscreen defcreateTurtle(): returnturtle.Turtle() defdrawSquare(t,step): foriinrange(4): t.forward(step) t.left(90) defexitOnClick(screen): screen.exitonclick() main() ✶如此,主函式的內容變得非常清楚,就是4項工作:產生螢幕、產生小烏龜、畫方形、及結束 ✶程式的可讀性更高了,如果不在乎個別函式的內容,甚至可以不需要了解(有可能是別的工程師負責撰寫,有可能在別的檔案), 我們只要看主函式就知道這份程式的功能了 ∗支援程式匯入而不立即執行 ▸我們所寫的程式也可以提供他人匯入使用,別人使用我們的程式的方式是先匯入,也就是不立即執行, 然後在需要的時候再呼叫我們所寫的函式,就如同我們匯入其他模組一樣:先匯入,需要時再執行 ▸但drawSquareModule.py程式最後一行是執行主函式,因此別人匯入時就會立即執行, 不符合匯入需求 ▸解決方案:以條件判斷是否要執行main()函式 ✶Python提供一個名為__name__的內建變數,如果一個程式檔案被執行了, __name__的值就會被設定為'__main__'字串 ✶因此,我們可以判斷__name__的值,確定程式是被匯入還是被執行, 如果是被執行就呼叫main()函式,否則就不呼叫 ✶修改程式: drawSquareModule.py ... defexitOnClick(screen): screen.exitonclick() main() if__name__=='__main__': main() #最後兩行:判斷__name__是否等於__main__, 如果是,就呼叫main()函式 ✶假設有另一個程式需要使用某個模組裡的某個函式或變數, 在該程式需要先匯入模組,然後再呼叫函式,呼叫語法為.()或 .,例如: callDrawSquareModule.py importturtle importdrawSquareModule defmain(): screen=turtle.Screen() screen.setup(400,300) screen.bgcolor('lightgray') myTurtle=turtle.Turtle() myTurtle.color('red') drawSquareModule.drawSquare(myTurtle,100) screen.exitonclick() if__name__=='__main__': main() #如此程式就更容易分享了 ✶註:匯入一個模組只需要寫主檔名,不需加上副檔名.py (6)匯入在其他套件裡的模組 ▸至目前為止,匯入與被匯入的模組都在同一目錄下,但專案常常會依不同功能建立不同的套件目錄, 例如以下專案結構: project/ packageA/ __init__.py modA.py ... packageB/ __init__.py modB.py ... ... ✶專案(Project)根目錄:project/ ✶套件(Package)目錄:packageA/,packageB/,...,因為這些目錄裡都有 __init__.py檔案(內容可以是空的),因此Python認定為套件,允許匯入模組 ✶模組(Module):__init__.py,modA.py,modB.py,... ▸假設在modB.py模組中匯入packageA 套件裡的modA模組: frompackageAimportmodA modA.drawSquare(...) ... ▸這樣的模組匯入需設定Python路徑,Python才找得到專案的目錄: ✶在Pyzo中設定Python路徑:Shell→Editshellconfiguration(編輯shell組態)→ pythonPath:(專案根目錄路徑)→Done ▸嘗試在if__name__=='__main__':指令前加上一行 print(__name__),分別觀察執行或匯入模組的__name__值 ▸練習3



請為這篇文章評分?