第15 章物件與類別 - Python

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

支援物件的程式語言稱為「物件導向程式語言」(Object-oriented programming language, OOPL),Python 屬此類語言. (2) 類別. ∗ 類別(Class). ▸ 每個物件都是某個 ... 序言 Python簡介 執行Python程式 Python的資料 除錯 小烏龜繪圖模組 Python模組 字串 串列與字組 字典 決策結構 迴圈 函式 檔案 例外處理 物件與類別 第15章  物件與類別 (1)物件簡介 ∗物件(Object): ▸物件用來描述一個物體、一件事、或一個人,物件內含: ✶資訊 ✶可執行的操作,用來處理資訊 ▸以物件的概念來解決問題,稱為「物件導向程式設計」(Object-orientedprogramming,OOP) ▸支援物件的程式語言稱為「物件導向程式語言」(Object-orientedprogramminglanguage,OOPL),Python屬此類語言 (2)類別 ∗類別(Class) ▸每個物件都是某個類別(Class)所產生的一個實例(Instance),就好像工廠裡一個模具可以產生許多相同規格的產品一般:模具就是類別,產品就是實例 ▸類別裡可包含屬性(Attribute)及方法(Method) ✶屬性:儲存物件的資訊,也就是變數 ✶方法:操作物件的資訊,也就是函式 ∗建立類別的語法 ▸一般類別: class: ... ✶例如:圓(Circle) circle.py(首先建立python/ch15目錄) classCircle: cx=0 cy=0 radius=0 defsetColor(self,color): self.color=color #類別名稱為Circle(通常使用大寫) #定義三個屬性:圓心位置座標(cx,cy) 及半徑(r),預設值都是0 #定義一個方法set_color():用來設定顏色,有兩個參數self 與color *類別方法的第一個參數一定是self( 為什麼一定要有這個參數?) *在方法中存取物件屬性需在屬性名稱前加上self.,此例中只要執行 set_color()方法,就會動態新增一個color屬性 *在方法中沒有self.前置的變數是區域變數,方法執行完畢即不存在,例如 set_color()方法中的紅色color: classCircle: defset_color(self,color): self.color=color ▸繼承類別(Inheritance):在類別名稱後加小括號,內含所繼承類別的名稱(稱為「父類別」,Parentclass) class(): ... ✶例如:Ball類別繼承Circle類別 classBall(Circle): cz=0 #類別名稱為Ball,繼承自Circle類別, 因此擁有該類別裡的所有屬性與方法:cx,cy,radius,setColor() #本類別另外再定義一個屬性:球心第三維座標cz (3)產生物件 ∗物件的產生 ▸語法:在類別名稱後加上一對小括號,並指派給一個變數即可產生一個實例 =() ▸()稱為建構子(Constructor),用來產生新的實例 ▸以Circle與Ball類別為例, 以下分別產生一個名為circle及ball的實例: circle=Circle() ball=Ball() ∗存取屬性與執行方法 ▸語法:都是利用點號 .   #存取屬性 .()    #執行方法 ▸例如:產生一個圓,列印半徑,設定圓心的cx座標為並列印,然後設定為紅色並列印 circle.py classCircle: ... defsetColor(self,color): self.color=color circle=Circle() print(circle.radius) circle.cx=20 print(circle.cx) circle.setColor('red') print(circle.color) 0 20 red ▸練習1 ∗物件的初始設定 ▸在產生物件時可以一併設定屬性:在類別裡加上__init__()初始方法,在產生實例時就會自動執行 class: def__init__(self,): ... ✶self之後的參數即為呼叫建構子時所傳入的參數 ✶__init__名稱是固定,不可以更改為其他名稱 ▸產生物件並傳入初始值之語法: =() ▸以Circle為例,在類別中加入__init__()方法, 用來設定圓心位置及半徑的初始值: circle.py classCircle: def__init__(self,cx,cy,radius): self.cx=cx self.cy=cy self.radius=radius defsetColor(self,color): self.color=color ... ✶__init__()方法有四個參數,其中self 是必須的,另外三個(cx,cy,radius)是輸入參數,用來設定屬性的初始值 ✶有了初始方法後,在產生物件時就需要提供建構子必要的參數(不含self),例如: circle=Circle(10,20,30)   #產生一個圓心位置在(10,20),且半徑為30的圓 ✶印出屬性值: circle.py classCircle: ... circle=Circle(10,20,30) print(circle.cx) print(circle.cy) print(circle.radius) 10 20 30 ▸練習2 (4)程序式設計與物件導向設計的比較:球的碰撞 ∗程序式設計(Proceduraldesign) defmain(): #建立球的資料 numBalls=30 xs=[]#位置x座標 ys=[]#位置y座標 rs=[]#半徑 cs=[]#顏色 ms=[]#質量 es=[]#彈性係數 ...#其他特性 foriinrange(numBalls): xs.append(...) ys.append(...) rs.append(...) cs.append(...) ms.append(...) es.append(...) ... #處理碰撞 foriinrange(numBalls-1): forjinrange(i+1,numBalls): collision(xs[i],ys[i],xs[j],ys[j],rs[i],rs[j],ms[i],ms[j],es[i],es[j]...) defcollision(x1,y1,x2,y2,r1,r2,m1,m2,e1,e2...): ... ▸將所有球的資料以串列表示,資料分散且紛亂 ▸處理球的碰撞時,需將所有資料以參數傳遞,資料冗長 ∗物件導向設計(Object-orienteddesign) #球類別 classBall: def__init__(self,x,y,r,c,m,e,...): self.x=x self.y=y self.r=r self.c=c self.m=m self.e=e ... defmain(): #建立球的資料 numBalls=30 balls=[] foriinrange(numBalls): balls.append(Ball(...)) #處理碰撞 foriinrange(numBalls-1): forjinrange(i+1,numBalls): collision(balls[i],balls[j]) defcollision(b1,b2): ... ▸每個球的資料均儲存在物件本身,程式只要傳遞球物件,所有資料就帶著走,存取非常方便,程式看起來也簡潔乾淨許多 (5)範例:模擬砲彈飛行路線 ∗程式開發流程: 1.定義問題 2.分析問題並推導出解決方案 3.依據解決方案撰寫第一版程式 4.第二版程式:模組化 5.第三版程式:物件化 ∗程式規格:模擬砲彈的飛行路線 ▸輸入:砲彈發射角度(度,degree),初始速度(公尺/秒,m/s),初始高度(公尺,m) ▸輸出:砲彈落地的距離(m) ∗問題分析 ▸假設忽略風阻,地心引力加速度為9.8m/s,亦即如果將一個物體以20m/s的速度垂直向上拋出, 一秒鐘之後,它的向上速度將減為20-9.8=10.2m/s,再過一秒則減為10.2-9.8=0.4m/s, 接著它很快的就要向下墜落了(當向上速度為0時) ▸可以利用微積分概念來計算在某個時間點物體的位置,本程式不用微積分, 而是利用模擬的方式來一點一點的追蹤砲彈 ▸計算砲彈的飛行需考慮兩項資料: ✶高度(Height):如此才能知道何時落地 ✶距離(Distance):以便追蹤砲彈飛了多遠 ▸砲彈的位置以二維資料表示:(px,py),px 為距離(預設為0),py為高度 ✶每個時間間隔追蹤一次砲彈位置:在此間隔中,砲彈向上移動了一些位置到達py, 並且向前移動了一些位置到達px ✶因為忽略風阻,因此x方向的移動速度是常數 ✶y方向的移動速度由地心引力控制:向上飛行為正數,向下墜落則為負數 ▸解決方案:虛擬程式碼 Inputthesimulationparameters:angle,velocity,height,interval Settheinitialpositionofthecannonball:px,py Calculatetheinitialvelocityofthecannonball:vx,vy Whilethecannonballisstillflying: updatethevaluesofpx,py,andvyforaninterval outputthedistancetraveledaspx ▸更新各項資料所需要使用的知識: ✶速度分解:三角學(你看,以前的老師不是說以後一定會用到嗎?) ✶物理學:距離等於速度乘以時間d=vt(你看,以前學的一定會用到!) #水平方向:速度不變,距離變化:px2=px1+ tvx #垂直方向:速度變化:vy2=vy1-9.8t, 距離變化:py2=py1+t(vy1+ vy2)/2 ▸虛擬程式碼轉為程式 cannonball.py importmath defmain(): angle=float(input('Launchangle(indegrees):')) v=float(input('Initialvelocity(inmeters/second):')) py=float(input('Initialheight(inmeters):')) interval=float(input('Timeinterval(inseconds):')) #Convertangletoradians theta=math.radians(angle) vx=v*math.cos(theta) vy=v*math.sin(theta) print('\nThetrajectory:') print('xy') print('--------------') #Loopuntilthecannonballhitstheground px=0.0 whilepy>=0.0: #Calculatepositionandvelocityinintervalseconds px+=interval*vx vy2=vy-interval*9.8 py+=interval*(vy+vy2)/2.0 vy=vy2 print(f'{px:>5.1f}\t{py:>5.1f}')#Printthetrajectoryofthecannonball print(f'\nDistancetraveled:{px:.1f}meters.') if__name__=='__main__': main() ✶float()函式會將使用者輸入的數值字串轉為浮點數 ✶print(...)函式: #:>5.1f表示向右對齊,總共五個字元,一位小數 #\t表示列印定位鍵(固定寬度) #:.1f表示顯示一位小數 ✶執行範例: Launchangle(indegrees):30 Initialvelocity(inmeters/second):30 Initialheight(inmeters):0 Timeinterval(inseconds):0.1 Thetrajectory: xy ------------- 2.61.5 5.22.8 . . 80.5-0.6 Distancetraveled:80.5meters. ∗第二版程式:模組化 ▸由上往下設計:將程式分解為三個模組 ✶輸入模組getInputs():取得使用者的輸入 ✶計算速度模組getXYComponents():計算x與y 方向的速度分量 ✶模擬砲彈路徑模組simulate():以迴圈更新資料方式模擬砲彈路徑,最後回覆砲彈飛行距離 ▸模組化程式 cannonball2.py importmath defmain(): angle,v,py,interval=getInputs() vx,vy=getXYComponents(v,angle) px=simulate(interval,py,vx,vy) print(f'\nDistancetraveled:{px:.1f}meters.') defgetInputs(): angle=float(input('Launchangle(indegrees):')) v=float(input('Initialvelocity(inmeters/second):')) py=float(input('Initialheight(inmeters):')) interval=float(input('Timeinterval(inseconds):')) returnangle,v,py,interval defgetXYComponents(v,angle): theta=math.radians(angle) vx=v*math.cos(theta) vy=v*math.sin(theta) returnvx,vy defsimulate(interval,py,vx,vy): print('\nThetrajectory:') print('xy') print('--------------') px=0.0 whilepy>=0.0: px+=interval*vx vy2=vy-interval*9.8 py+=interval*(vy+vy2)/2.0 vy=vy2 print(f'{px:>5.1f}\t{py:>5.1f}')#Printthetrajectoryofthecannonball returnpx if__name__=='__main__': main() ✶此版本程式較為簡潔: #主程式變數的數量由原先的10個減少為8個(theta及 vy變成區域變數),將暫時的資料隱藏在區域中,有助於由上往下的設計模式 ✶但程式還是有點複雜,特別是迴圈: #追蹤砲彈狀況需要5項資訊(interval,px,py,vx,vy),其中3項(px,py,vy)每次都需要修改 #變數數量太多,表示程式還有改善的空間 ∗第三版程式:物件化 ▸目前程式使用4個變數來描述砲彈(px,py,vx,vy),這些變數分佈在各個模組裡, 讓程式看來很紛亂 ✶假設規模稍大的問題,裡面有20個砲彈,那麼變數就需要使用串列px[20],py[20],vx[20],vy[20], 再加上如果變數很多(砲彈尺寸、重量、顏色、外觀、名稱...),我們可以想像得到,那將會是多少變數、多少串列元素在程式裡滿天飛!! ▸如果我們有一個砲彈(Cannonball)物件類別,所有描述砲彈的變數就是砲彈物件的屬性, 這些屬性是封在砲彈物件裡的,因此我們只會有一個串列變數cannonball[20], 要讀取砲彈的某個變數只要用點號即可(例如:cannonball[10].px), 程式乾淨許多 ▸另外,如果我們好好設計這個砲彈類別,讓它「了解」物體的物理特性,也有模擬彈道的方法, 那麼主程式就只需要建立該物件並且呼叫模擬方法即可,問題結構變得單純許多: importmath defmain(): angle,v,py,interval=getInputs() cannonball=Cannonball(angle,v,py) cannonball.simulate(interval) print(f'\nDistancetraveled:{cannonball.px:.1f}meters.') ✶很顯然的,此版本更為簡潔,而且清楚說明演算法的涵義: #先收集必要資訊 #產生一個砲彈物件 #呼叫砲彈物件的模擬彈道方法 #最後印出結果 ✶因此只要定義適當Cannonball類別,然後實作 simulate()方法即可,如下: cannonball3.py: importmath classCannonball: def__init__(self,angle,velocity,height): self.px=0.0 self.py=height theta=math.radians(angle) self.vx=velocity*math.cos(theta) self.vy=velocity*math.sin(theta) defsimulate(self,interval): print('\nThetrajectory:') print('xy') print('--------------') whileself.py>=0: self.px+=interval*self.vx vy2=self.vy-9.8*interval self.py+=interval*(self.vy+vy2)/2.0 self.vy=vy2 print(f'{self.px:>5.1f}\t{self.py:>5.1f}') defmain(): angle,v,h0,interval=getInputs() cannonball=Cannonball(angle,v,h0) cannonball.simulate(interval) print(f'\nDistancetraveled:{cannonball.px:.1f}meters.') defgetInputs(): angle=float(input('Launchangle(indegrees):')) v=float(input('Initialvelocity(inmeters/second):')) py=float(input('Initialheight(inmeters):')) interval=float(input('Timeinterval(inseconds):')) returnangle,v,py,interval if__name__=='__main__': main()



請為這篇文章評分?