python程序設計 課件 第10章 面向對象編程_第1頁
python程序設計 課件 第10章 面向對象編程_第2頁
python程序設計 課件 第10章 面向對象編程_第3頁
python程序設計 課件 第10章 面向對象編程_第4頁
python程序設計 課件 第10章 面向對象編程_第5頁
已閱讀5頁,還剩84頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第10章面向對象程序設計Python程序設計第10章面向對象程序設計在本章中我們來學習面向對象編程(Object-OrientedProgramming、OOP)的一些重要概念。對象是結合了數(shù)據(jù)和操作的計算實體,包括相關數(shù)據(jù)的集合以及該數(shù)據(jù)的一系列操作。對象的數(shù)據(jù)存儲在實例變量中并通過方法進行操作。每個對象都是某個類的一個實例。類定義確定了對象的屬性是什么,對象將具有什么方法。通過調用構造函數(shù)方法創(chuàng)建實例,可以通過編寫合適的類定義來創(chuàng)建新類型的對象。面向對象的概念1類的方法4類與對象2構造函數(shù)與對象初始化3示例程序:發(fā)射炮彈6目錄類作用域510.1

面向對象的概念10.1

面向對象的概念此前,我們一直在使用Python內置的數(shù)字和字符串數(shù)據(jù)類型來編寫程序。每個數(shù)據(jù)類型可以表示一組特定的值,并且每個數(shù)據(jù)類型都有一組相關的操作。從傳統(tǒng)的計算視角出發(fā),我們將數(shù)據(jù)視為一些被動實體,通過主動操作來控制和組合它們。為了進一步地構建復雜系統(tǒng),我們需要采用更豐富的視角來看待數(shù)據(jù)和操作之間的關系。面向對象程序設計(ObjectOrientedProgramming,OOP)是一種計算機編程架構,它的一條基本原則是計算機程序由單個能夠起到子程序作用的單元或對象組合而成。如今,大多數(shù)現(xiàn)代計算機程序是用“面向對象”方法構建的,其中的核心概念是類和對象。10.1

面向對象編程的概念“以對象為基礎”的程序設計方式,起源于1960年代末期的Simula語言,經過多年發(fā)展研究,例如Smalltalk、C++、Java,逐步發(fā)展出面向對象的三大基本概念:封裝、繼承和多態(tài),都圍繞著控制大型軟件程序的復雜性、程序代碼重復使用、穩(wěn)定且易于維護修改等要求。我們真正想要的是在開發(fā)軟件時,能夠控制程序代碼的復雜性,編寫出穩(wěn)定的程序,易于維護和修改,又希望能夠重復使用。面向對象程序設計方法是盡可能模擬人類的思維方式,使得軟件的開發(fā)方法與過程盡可能接近人類認識世界、解決現(xiàn)實問題的方法和過程,也即使得描述問題的問題空間與問題的解決方案空間在結構上盡可能一致,把客觀世界中的實體抽象為問題域中的對象。10.2類與對象對象的定義類的定義對象的建立10.2

類與對象面向過程程序設計把程序視為一組過程(模塊、函數(shù))的集合,每個過程都可以接收并處理其他過程發(fā)出的消息,程序的執(zhí)行就是一系列消息在各個過程之間的加工和傳遞。面向對象程序設計則是以對象為核心,把對象作為程序的基本單元,對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。為了簡化程序設計,面向對象過程把函數(shù)繼續(xù)切分為子函數(shù)來降低系統(tǒng)的復雜度。換句話說,是將“對象”定義為一種主動的數(shù)據(jù)類型,它知道一些事情并可以做一些事情。10.2.1對象的定義一個對象包括:(1)一組相關的信息。(2)操作這些信息的一組操作(又稱為“方法”)。信息存儲在對象的“實例變量”中,操作是“存在”于對象中的函數(shù),實例變量和操作一起被稱為對象的“屬性”。事實上,Python中的所有數(shù)據(jù)類型都可以視為對象,當然也可以自定義對象。自定義的對象數(shù)據(jù)類型就是面向對象中的類(class)的概念。10.2.1對象的定義我們以一個處理學生成績表的例子來說明面向對象的程序流程。面向對象程序設計首先考慮Student這種數(shù)據(jù)類型應該被視為一個對象,這個對象擁有name和score這兩個屬性。如果要打印一個學生的成績,首先必須創(chuàng)建出這個學生對應的對象的實例,然后給該對象實例發(fā)一個print_score消息,讓對象實例自己把數(shù)據(jù)打印出來。classStudent(object):def__init__(self,name,score):=nameself.score=scoredefprint_score(self):print('%s:%s'%(,self.score))10.2.1對象的定義給對象發(fā)消息實際上就是調用對象對應的關聯(lián)函數(shù)(方法)。面向對象程序以如下形式實現(xiàn):ming=Student('xiaoming',59)fang=Student('xiaofang',87)ming.print_score()fang.print_score()10.2.2類的定義類是對現(xiàn)實世界的抽象,包括表示靜態(tài)屬性的數(shù)據(jù)和對數(shù)據(jù)的操作,對象是類的實例化。對象間通過消息傳遞相互通信,來模擬現(xiàn)實世界中不同實體間的聯(lián)系。以Python的class語句來自行定義類,其語法如下:class類名(父類,...):

語句下面則是最簡單的類定義classMyClass():pass首先以關鍵字class開頭,之后是由你取的新類名稱,后面括號內可放進此類欲繼承的父類,然后是冒號“:”,之后跟著眾多程序語句,此例除了pass語句,其他什么都沒有。10.2.2類的定義我們知道在Python里所有東西都是對象,整數(shù)、浮點數(shù)、列表是對象,甚至模塊、函數(shù)、類也是對象,所以上面的類定義,將會產生出類對象。既然是對象,也就具有命名空間的功能,可放入名稱指向各種其他對象,稱為屬性項。>>>classMyClass(): #類定義...pass...>>>MyClass #名稱MyClass指向類型為class的對象<class'__main__.MyClass'>>>>MyClass.pi=3.14 #具有命名空間的功能>>>MyClass.pi #屬性項pi指向整數(shù)對象3.143.14>>>deffoo():print('hello')10.2.2類的定義...>>>MyClass.bar=foo #MyClass屬性項bar指向函數(shù)對象>>>MyClass.bar<functionfooat0x00C73228>10.2.2類的定義10.2.2類的定義不過,我們通常會以class語句定義新的類,定義時便決定好里頭含有的數(shù)據(jù)成員與方法界面,然后以之作為樣板建立許多對象。例如讓我們定義銀行賬戶類:classBankAccount(): #定義類

def__init__(self): #初始化

self.balance=0 #建立名稱balance,代表余額

defdeposit(self,amount): #存款

self.balance+=amountdefwithdraw(self,amount):#提款

self.balance-=amountdefget_balance(self):#顯示余額

returnself.balance10.2.2類的定義ac0=BankAccount() #構造函數(shù),建立銀行賬戶對象print(ac0.balance) #直接存取余額ac0.deposit(100) #存款100ac0.withdraw(30) #提款30print(ac0.get_balance()) #透過方法得到余額ac1=BankAccount() #建立另一個銀行賬戶對象ac1.deposit(1000)print(ac1.get_balance())10.2.2類的定義10.2.2類的定義建立實例后,便可通過類定義中的方法來存取,例如deposit代表存款、withdraw代表提款。嚴格說來,deposit與withdraw只是函數(shù)而已,但使用“實例.方法(參數(shù))”的語法時,Python解釋器會轉成“函數(shù)(實例,參數(shù))”的形式,為此,我們在定義方法時,第一個參數(shù)self是特別的,代表實例對象本身,而在執(zhí)行方法時,便是通過self來存取該實例對象里的屬性項,也就是各種數(shù)據(jù)(例如賬戶余額)與其他方法。對象的屬性通過點符號訪問。Python中,類中方法定義的第一個參數(shù)始終是self,表示引用方法的對象自身。>>>classMyClass(): #類定義...def__init__(self): #__init__函數(shù)是內置方法...self.x=3...deffoo(self):...self.x+=1...print(self.x)...defbar(abc):...abc.x-=1...print(abc.x)10.2.2類的定義...>>>c=MyClass() #建立實例>>>c.foo() #調用方法4>>>MyClass.foo(c) #調用函數(shù)5>>>c.bar(c) #調用函數(shù)410.2.2類的定義10.2.2類的定義在上面例子中,若是“c.foo()”的語法,我們會把foo當作方法,若是“MyClass.foo(c)”的語法,則把foo當作函數(shù),其實沒什么不同,方法就是作為類屬性項的函數(shù),而“實體.方法(參數(shù))”的語法更為直觀方便。10.2.3對象的建立面向對象的設計思想是抽象出類,根據(jù)類創(chuàng)建實例。面向對象的抽象程度比函數(shù)要高,因為類既包含數(shù)據(jù),又包含操作數(shù)據(jù)的方法。面向對象最重要的概念就是類和實例,必須牢記類是抽象的模板,實例是根據(jù)類創(chuàng)建出來的一個個具體的“對象”,每個對象擁有相同的方法,但各自的數(shù)據(jù)可能不同。仍以Student類為例,在Python中,定義類是通過class關鍵字:classStudent(object):passclass后面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的。通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。10.2.3對象的建立定義好了Student類,就可以根據(jù)Student類創(chuàng)建出Student的實例,創(chuàng)建實例是通過“類名+()”實現(xiàn)的:>>>ming=Student()>>>ming<__main__.Studentobjectat0x10a67a590>>>>Student<class'__main__.Student'>可以看到,變量ming指向的就是一個Student的實例,后面的0x10a67a590是內存地址,每個object的地址都不一樣,而Student本身則是一個類。10.2.3對象的建立可以自由地給一個實例變量綁定屬性,比如,給實例ming綁定一個name屬性:>>>='BartSimpson'>>>'xiaoming'10.2.3對象的建立由于類可以起到模板的作用,因此,可以在創(chuàng)建實例的時候,把一些我們認為必須綁定的屬性填寫進去。通過定義一個特殊的__init__方法(前后分別有兩個下劃線),在創(chuàng)建實例的時候,需要同時綁定name,score等屬性:classStudent(object):def__init__(self,name,score):=nameself.score=score__init__方法的第一個參數(shù)永遠是self,表示創(chuàng)建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,self就指向創(chuàng)建的實例本身。10.2.3對象的建立有了__init__方法,在創(chuàng)建實例的時候,就不能傳入空的參數(shù)了,必須傳入與__init__方法匹配的參數(shù),但self不需要傳,Python解釋器自己會把實例變量傳進去:>>>ming=Student('xiaoming',59)>>>'xiaoming'>>>ming.score59和普通的函數(shù)相比,在類中定義的函數(shù)只有一點不同,就是第一個參數(shù)永遠是實例變量self,并且調用時不用傳遞該參數(shù)。除此之外,類的方法和普通函數(shù)沒有區(qū)別,你仍然可以用默認參數(shù)、可變參數(shù)、關鍵字參數(shù)和命名關鍵字參數(shù)。10.3

構造函數(shù)與對象初始化10.3

構造函數(shù)與對象初始化建立實例時,大致上可分為兩個階段,首先由Python解釋器配置存儲器空間、設置基本的實例對象,然后會調用名為__init__的方法,由它進行實例對象的初始化。因為我們通常不會去涉及第一階段的行為,所以往往直接稱呼__init__為構造函數(shù)。__init__的主要工作就是建立實例應該含有的數(shù)據(jù),即“第一次指派”,在實例對象里產生名稱,指向初始值(對象)。classPerson():def__init__(self,name,age,height,weight):=nameself.age=ageself.height=heightself.weight=weight#p1=Person() #錯誤,參數(shù)不足p2=Person('John',22,170,60) #建立實例p3=Person('Amy',17,160,42) #建立另一個實例10.3

構造函數(shù)與對象初始化10.3

構造函數(shù)與對象初始化每個實例各自獨立,都擁有它自己的命名空間,只要在__init__里進行指派動作,便能產生存在于實例里的名稱(指向其他對象),例如上例中會有name、age、height、weight;p2含有name,p3也有它自己的name;但因為p2與p3的類別都是Person,所以擁有相同的接口。10.4

類的方法10.4

類的方法方法是實例對象的接口,外界透過方法來存取實例,是實例與外界溝通的管道。下面以Book類為例,實例存儲了三個數(shù)據(jù):書名title、定價cover_price、折扣discount,然后定義各種存取方法。在class語句里定義的名稱,如set_discount、get_price等,都成為該類的屬性項,若是函數(shù)的話,將會成為實例對象的方法。classBook():def__init__(self,title,cover_price,discount=1.0):self.title=titleself.cover_price=cover_priceself.discount=discountdefget_title(self):returnself.titledefget_cover_price(self):returnself.cover_pricedefget_discount(self):returnself.discountdefset_discount(self,discount):self.discount=discountdefget_price(self):returnint(self.cover_price*self.discount)10.4

類的方法b1=Book('MakeWishes',300) #建立書本實例,折扣使用缺省值1.0b2=Book('Funofcooking',500,0.79) #建立書本實例,打79折print(b2.get_price())10.4

類的方法10.4

類的方法當我們使用“實例.方法(參數(shù))”的語法時,看起來好像是把信息(參數(shù))傳給該實例對象,而該實例對象操作其他對象時,也是通過同樣的方式,所以有人稱之為“信息傳遞”。10.5

類作用域10.5

類作用域前面詳細介紹過作用域與命名空間的概念,例如全局作用域、局部(函數(shù))作用域,而class語句也有其類作用域,所以在class語句內定義的內容,都會成為該類別的屬性項,其名稱存在于類別對象的命名空間內,指向各種對象。classMyClass():x,y,z=3,4,5defsfoo():#print(z) #出錯

print(MyClass.z)def__init__(self,x,y):self.x=xself.y=ydeffoo(self):print(self.x+self.z)10.5

類作用域defbar(self):#print(self.y-z) #出錯

print(self.y-MyClass.z)c0=MyClass(33,44)MyClass.sfoo() #輸出5c0.foo() #輸出38c0.bar() #輸出39#c0.sfoo() #出錯10.5

類作用域10.5

類作用域在上面的例子中,MyClass類擁有屬性項x、y、z、__init__、sfoo、foo、bar,由所有實例共享。而MyClass(33,44)所建立出來的實例,則擁有屬性項x、y,請注意類與實體都有名為x和y的屬性項。class語句雖有其類作用域,但并不能像函數(shù)作用域成為內部函數(shù)的外圍作用域,所以,透過MyClass.sfoo()執(zhí)行sfoo函數(shù)時,里頭的print(z)會出現(xiàn)錯誤信息“NameError:globalname'z'isnotde?ned(名稱錯誤:全局名稱z并未定義)”,因為執(zhí)行時的環(huán)境指向全局作用域,自然無法存取MyClass類作用域里的名稱,此時,可透過名稱MyClass存取里頭的z,類與實例各自擁有自己的命名空間。

類與實例的屬性項10.5

類作用域同樣的,執(zhí)行c0.bar()時,其實就等同于MyClass.bar(c0),同樣看不到z,所以print(self.y-z)也會出錯。不過若是c0.foo()的話,透過self來存取時,會先到實例命名空間里尋找,找不到z,然后從類的命名空間里找到z,可正常執(zhí)行。另外,若執(zhí)行c0.sfoo()將會出現(xiàn)參數(shù)過多的錯誤,因為會變成MyClass.sfoo(c0),而sfoo并不需要參數(shù)c0,所以出錯。類的屬性項被所有實例共享,通常是方法,若是一般函數(shù)的話,則應該是不需要實例也能執(zhí)行的功能,另外也可以是一般數(shù)據(jù)類型,例如整數(shù)。而實例的屬性項則由該實例擁有,每個實例都擁有它自己的一份,各自獨立。類作用域跟之前介紹的函數(shù)(局部)作用域不太一樣,并不具備所謂“靜態(tài)作用域”能力,內部的函數(shù)或方法,并不能存取外部(類)的屬性項。deffoo(): #函數(shù)

x=3defbar(): #內部函數(shù)

print(x) #可以

bar()classMyClass(): #類

x=3deffoo():print(x) #A處,若執(zhí)行會出錯

print(MyClass.x) #可以10.5

類作用域defbar(self):print(x) #B處,若執(zhí)行出錯

print(self.x) #可以#MyClass.foo() #A處出錯mc=MyClass()mc.bar() #B處出錯10.5

類作用域10.5

類作用域總之,類里的函數(shù)與方法,其記錄的環(huán)境并不包括類的命名空間,因此,想存取函數(shù)和方法時,必須加上類名或指向實例的名稱(例如self)。程序規(guī)格說明設計程序程序模塊化Projectile類的定義用類數(shù)據(jù)處理10.6示例程序:發(fā)射炮彈10.6

示例程序:發(fā)射炮彈下面,我們通過一個發(fā)射炮彈的實例來熟悉定義類的操作。10.6.1程序規(guī)格說明假設我們希望編寫一個模擬炮彈(或任何其他拋體,如子彈、棒球或鉛球)的程序。我們感興趣的是,確定在各種發(fā)射角度和初始速度下,炮彈將飛多遠。程序的輸入是炮彈的發(fā)射角(以度為單位)、初始速度(以米每秒為單位)和初始高度(以米為單位)。輸出是拋體在撞擊地面前飛行的距離(以米為單位)。如果忽略風阻的影響,并假設炮彈靠近地球表面,這是一個相對簡單的經典物理問題。地球表面附近的重力加速度約為9.8米/秒2。這意味著如果一個物體以每秒20米的速度向上拋出,經過一秒鐘之后,它的向上速度將會減慢到20-9.8=10.2米/秒。再過一秒,速度將只有0.4米/秒,之后不久就會開始回落。我們的程序是用模擬來跟蹤炮彈每個時刻的位置。利用一點簡單的三角學以及一個明顯的關系,即物體在給定時間內飛行的距離等于其速率乘以時間(d=rt),用算法解決這個問題。10.6.2設計程序先設計一個算法。我們需要考慮炮彈的兩個維度:一是高度,這樣可以知道什么時候碰到地面。二是距離,記錄它飛多遠。可以將炮彈的位置視為二維圖中的點(x,y),其中y的值給出了地面之上的高度,x的值給出了與起點的距離。模擬程序必須更新炮彈的位置來說明它的飛行。假設炮彈從位置(0,0)開始,希望定時檢查它的位置,比如說,每隔十分之一秒。在那段時間內,它將向上移動一些距離(正y)并向前移動一些距離(正x)。每個維度的精確距離由它在該方向上的速度決定。分離速度的x和y分量讓問題更容易。由于忽略風阻,所以x速度在整個飛行中保持不變。然而,由于重力的影響,y速度隨時間而變化。事實上,y速度開始為正,然后隨著炮彈開始下降,會變?yōu)樨撝怠?0.6.2設計程序根據(jù)這樣的分析,模擬要做什么就清楚了。下面是粗略的算法大綱:輸入模擬參數(shù):角度,速度,高度,間隔計算炮彈的初始位置:xpos,ypos計算炮彈的初始速度:xvel,yvelwhile炮彈仍在飛行:

將xpos,ypos和yvel的值更新為飛行輸出中距離作為xpos的距離我們逐步求精,將它變成一個程序。10.6.2設計程序算法的第一行很簡單,只需要合適的輸入語句序列。下面是開始:defmain():angle=float(input("輸入發(fā)射角度(以度為單位):"))vel=float(input("輸入初始速度(以米/秒為單位):"))h0=float(input("輸入初始高度(以米為單位):"))time=float(input("輸入位置計算之間的時間間隔:"))計算炮彈的初始位置也很簡單。它將從距離0和高度h0開始。我們只需要兩句賦值語句:xpos=0.0ypos=h010.6.2設計程序接下來,需要計算初始速度的x和y分量。我們需要一點三角學知識。如果我們認為初始速度由y方向的一些分量和x方向的一些分量組成,那么這三個量(速度,x速度和y速度)形成一個直角三角形。下圖說明了這種情況。如果我們知道速度的大小和發(fā)射角度(標記為θ,因為希臘字母θ經常用作角度的度量),我們可以通過公式xvel=速度+cos(θ),很容易地計算xvd的大小,類似的公式(使用sin(θ)計算出yvel。

計算速度的x和y分量10.6.2設計程序我們的輸入角度以度為單位,Python數(shù)學庫采用弧度度量。應用公式之前,必須轉換角度。圓周有2π弧度(360度),所以θ=(π*角度)/180。這是常見的轉換,math庫提供了一個方便的函數(shù),稱為radians,用于執(zhí)行這種計算。這三個公式給出了計算初始速度的代碼:theta=math.radians(angle)xvel=velocity*math.cos(theta)yvel=velocity*math.sin(theta)10.6.2設計程序接下來是程序的主循環(huán)。我們希望不斷更新炮彈的位置和速度,直到它到達地面。我們可以通過檢查ypos的值來做到這一點: whileypos>=0.0:使用>=可以從炮彈在地面上開始(=0),仍然讓循環(huán)執(zhí)行。一旦ypos的值下降到0以下,循環(huán)就會退出,表明炮彈己經略微嵌入地面了。10.6.2設計程序現(xiàn)在來到模擬的關鍵。通過循環(huán)希望每次更新炮彈的狀態(tài),讓它在飛行中移動time秒。先從水平方向考慮運動。由于規(guī)格說明指出可以忽略風阻,所以炮彈的水平速度將保持不變,由xvel的值給出。作為一個具體的例子,假設炮彈以30米/秒的速度飛行,目前距離發(fā)射點50米。下1秒鐘,它將再次前進30米,距離發(fā)射點80米。如果間隔時間只有0.1秒(而不是l秒),那么炮彈只飛行0.1*30=3米,距離為53米。可以看到,飛行的距離總是由time*xvel給出。要更新水平位置,只需要一個語句: xpos=xpos+time*xvel10.6.2設計程序垂直分量的情況稍微復雜一些,因為重力會導致y速度分量隨時間而變化。每秒必須減少9.8米/秒,即重力加速度。在0.1秒內,速度將減少0.1*9.8=0.98米/秒。間隔結束時的新速度計算為: yvel=yvel+time*9.8為了計算在這個時間間隔內炮彈的飛行的距離,需要知道它的“平均”垂直速度。由于重力加速度是恒定的,所以平均速度就是開始和結束速度的平均值(yvel+yvell)/2.0。平均速度乘以間隔時間,給出了高度的變化。10.6.2設計程序下面是完成的循環(huán):whileypos>=0.0:xpos=xpos+time*xvelyvell=yvel-time*9.8ypos=ypos+time*(yvel+yvell)/2.0yvel=yvell注意,時間間隔結束時的速度先存儲在臨時變量yvell中。這是為了保持初始值,從而可以用兩個值計算平均速度。最后,在循環(huán)結束時,將yvel賦予新值。這表示在間隔結束時炮彈的正確垂直速度。程序的最后一步就是輸出飛行距離。添加此步驟得到了完整的程序。【程序實例10-1】設計一個發(fā)射炮彈的程序。#cball1.pyfrommathimportsin,cos,radiansdefmain():angle=float(input("輸入發(fā)射角度(以度為單位):"))vel=float(input("輸入初始速度(以米/秒為單位):"))h0=float(input("輸入初始高度(以米為單位):"))time=float(input("輸入位置計算之間的時間間隔(以秒為單位):"))#將角度轉換為弧度

theta=radians(angle)10.6.2設計程序#設置x和y方向的初始位置和速度

xpos=0ypos=h0xvel=vel*cos(theta)yvel=vel*sin(theta)#循環(huán)直到球擊中地面

whileypos>=0.0:#以秒為單位計算位置和速度10.6.2設計程序xpos=xpos+time*xvelyvell=yvel-time*9.8ypos=ypos+time*(yvel+yvell)/2.0yvel=yvellprint("\n飛行距離:{0:0.1f}米。".format(xpos))main()10.6.2設計程序10.6.3程序模塊化上面我們采用了自頂向下、逐步求精的設計方法,但是沒有將程序分成單獨的函數(shù)。下面我們將以兩種不同的方式對程序進行模塊化。首先使用函數(shù)(即自頂向下設計)。下面是使用輔助函數(shù)的主算法版本:defmain():angle,vel,h0,time=getInputs()xpos,ypos=0,h0xvel,yvel=getXYComponents(vel,angle)whi1eypos>=0:xpos,ypos,yvel=updateCannonBall(time,xpos,ypos,xvel,yvel)print("\n飛行距離:{0:0.1f}米。".format(xpos))10.6.3程序模塊化根據(jù)這些函數(shù)的名稱和原來的程序代碼,這些函數(shù)做什么應該是明顯的。你需要花點時間來編寫三個輔助函數(shù)。第二個版本的主算法肯定比較簡潔。變量己經減少到8個,theta和yvell函數(shù)從主算法中消除了,因為只有在getXYComponents內部才需要theta的值。同樣,yvell作為updateCannonBall的局部變量了。關注點分離的主要好處是能夠隱藏一些中間變量。10.6.3程序模塊化但是,這個版本似乎也過于復雜。特別是循環(huán),記錄炮彈的狀態(tài)需要四條信息,其中三條必須隨時改變。需要所有四個變量以及時間的值來計算三個變量的新值,一個函數(shù)調用有五個參數(shù)和三個返回值。參數(shù)過多通常表明程序仍有改進空間。我們來試試另一種方法。在當前程序中,描述炮彈對象需要xpos、ypos、xvel和yvel四個信息。假設我們有一個Projectile類,能“理解”炮彈這類物體的物理特性。利用這樣的類,可以用單個變量來創(chuàng)建和更新合適的對象,表示主算法。10.6.3程序模塊化通過這種“基于對象”的方法,我們可以這樣編寫主程序:defmain():angle,vel,h0,time=getInputs()cball=Projectile(angle,vel,h0)whilecbaii.getY()>=0:cball.update(time)print("\n飛行距離:{0:0.1f}米。".format(Cball.getX()))10.6.3程序模塊化顯然,這是比較簡單而直接表達的算法。angle、vel和h0的初始值作為參數(shù),創(chuàng)建了一個名為cball的Projectile實例。每次通過循環(huán)時,都會要求cball更新其狀態(tài)以記錄時間。我們可以隨時用它的getX和getY方法來獲取cball的位置。為了讓它工作,我們只需要定義一個合適的Projectile類,讓它實現(xiàn)update、getX和getY方法。10.6.4Projectile類的定義在炮彈的例子中,我們希望得到一個可以代表拋體的類。這個類需要一個構造方法來初始化實例變量,一個update方法來改變拋體的狀態(tài),以及getX和getY方法讓我們得知當前的位置。我們從構造方法開始。在主程序中,將用最初的角度、速度和高度創(chuàng)建一個炮彈: cball=Projectile(angle,vel,h0)Projectile必須有一個__init__方法,用這些值來初始化cball的實例變量。實例變量包含xpos、ypos、xvel和yvel四種信息,表示炮彈飛行的特征。我們將使用原來程序中的相同公式來計算這些值。10.6.4Projectile類的定義下面是帶有構造方法的類:classProjectile:def__init__(self,angle,velocity,height):self.xpos=0.0self.ypos=heighttheta=math.radians(angle)self.xvel=velocity*math.cos(theta)self.yvel=velocity*math.sin(theta)在對象內創(chuàng)建了包含self在內的四個實例變量。在__init__結束后,就不需要theta的值,所以theta就成為一個普通的(局部的)函數(shù)變量。10.6.4Projectile類的定義獲取拋體位置的方法很簡單:當前位置由實例變量xpos和ypos給出,只需要一些返回這些值的方法。defgetX(self):returnself.xposdefgetY(self):returnself.ypos10.6.4Projectile類的定義最后來看update方法。該方法接受一個普通參數(shù),表示時間間隔。我們需要更新拋體的狀態(tài),以反映這段時間的流逝。下面是代碼:defupdate(self,time):self.xpos=self.xpos+time*self.xvelyvell=self.yvel-time*9.8iypos+time*(self.yvel+yvell)/2.0這些基本上是原來程序中使用的代碼,修改后成為使用和修改實例變量。注意使用yvell作為臨時(普通)變量。在方法最后一行,將該值存儲到對象中,從而保存該新值。10.6.4Projectile類的定義方法的定義看起來就像一個普通的函數(shù)定義。將函數(shù)放在類中使其成為該類的方法,而不是獨立的函數(shù)。注意到類中定義的每個方法都有一個名為self的第一個參數(shù)。方法的第一個參數(shù)是特殊的:它總是包含該方法所在的對象的引用。像往常一樣,你可以用任何合法的名字來命名這個參數(shù),但通常用self表示。10.6.4Projectile類的定義方法調用是一種函數(shù)調用,像普通函數(shù)調用一樣,Python執(zhí)行一個四步序列:第一步,調用程序(main)暫停在方法調用處。Python在對象的類中,找到合適的方法定義,將該方法應用于該對象。第二步,該方法的形參被賦予調用的實參提供的值。在方法調用時,第一個形參對應于該對象。第三步,執(zhí)行方法體。第四步,控制返回到調用方法之后的位置。10.6.4Projectile類的定義為了避免混淆,應該始終將方法的第一個形式參數(shù)稱為self參數(shù),任何其他參數(shù)作為普通參數(shù)。在概念上,實例變量提供了一種記住對象內的數(shù)據(jù)的方法。與常規(guī)變量一樣,實例變量通過名稱來訪問。我們可以用熟悉的<object>.<instance-var>形式來表示。實例變量的強大之處在于,可以用它們來記住特定對象的狀態(tài),然后將該信息作為對象的一部分傳遞給程序。實例變量的值可以在其他方法中引用,甚至在連續(xù)調用相同方法時再次引用。這與常規(guī)的局部函數(shù)變量不同,一旦函數(shù)終止,其值將消失?!境绦驅嵗?0-2】完成拋體類,建立一個完整的基于對象的方案來解決炮彈問題。#cball2.pyfrommathimportsin,cos,radiansclassProjectile:def__init__(self,angle,velocity,height):self.xpos=0.0self.ypos=heighttheta=radians(angle)self.xvel=velocity*cos(theta)self.yvel=velocity*sin(theta)10.6.4Projectile類的定義10.6.5用類數(shù)據(jù)處理拋體的例子展示了類的用處,它們針對具有復雜行為的真實世界對象進行建模。對象的另一個常見用途是將一組描述人或事的信息組合在一起。例如,公司需要記錄所有員工的信息。他們的員工系統(tǒng)可能會使用Employee對象,包含員工姓名、身份證號碼、地址、工資、部門等數(shù)據(jù)。這種信息分組通常稱為“記錄”。10.6.5用類數(shù)據(jù)處理讓我們來看一下涉及大學生的一些簡單數(shù)據(jù)處理。在典型的大學里,課程是按學分來衡量的,而平均分是以4分為基準計算的,其中“A”是4分,“B”是3分,等等。平均積分點(GPA)計算采用積分點。如果課程價值3個學分,學生獲得“A”,那將獲得3(4)=12個積分。要計算學生的平均積分點,我們將總積分點除以完成的學分數(shù)。10.6.5用類數(shù)據(jù)處理假設我們有一個包含學生成績信息的數(shù)據(jù)文件。文件的每一行都包含一個學生的姓名、學分和積分點。這三個值由制表符分隔。例如,文件的內容可能像下面這樣:Adams,Henry 127 228Computewell,Susan 100 400DibbleBit,Denny 18 41.5Jones,Jim 48.5 155Smith,Frank 37 125.3310.6.5用類數(shù)據(jù)處理我們編寫一個程序,讀取這個文件,找到GPA最好的學生,打印他的名字、學分和GPA。我們可以先創(chuàng)建一個student類,其對象是單個學生的信息記錄。在這個例子中,學生信息包括名稱、學分和積分點,可以將這些信息作為實例變量保存,在構造方法中初始化:classstudent:def__init__(self,name,hours,qpoints):=nameself.hours=float(hours)self.qpoints=float(qpoints)請注意,這里使用了與實例變量名匹配的參數(shù)名稱,對于這種類來說,這是一種很常見的風格。學時和積分的值設置成浮點數(shù),這讓構造方法變得更通用,它可以接受浮點數(shù)、整數(shù)甚至字符串作為參數(shù)。10.6.5用類數(shù)據(jù)處理有了一個構造方法,就很容易創(chuàng)建學生記錄。例如,可以為HenryAdams創(chuàng)造一個記錄: aStudent=student("Adams,Henry",127,228)使用對象允許我們在單個變量中收集有關個人的所有信息。10.6.5用類數(shù)據(jù)處理接下來,我們必須決定student對象應該有什么方法。顯然,我們希望能夠訪問學生的信息,所以定義一組取值方法。defgetName(self):returndefgetHours(self):returnself.hoursdefgetQPoints(self):returnself.qpoints10.6.5用類數(shù)據(jù)處理這些方法使我們能夠從學生記錄中獲取信息

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論