Python3程序設(shè)計實戰(zhàn)教程第7章 類_第1頁
Python3程序設(shè)計實戰(zhàn)教程第7章 類_第2頁
Python3程序設(shè)計實戰(zhàn)教程第7章 類_第3頁
Python3程序設(shè)計實戰(zhàn)教程第7章 類_第4頁
Python3程序設(shè)計實戰(zhàn)教程第7章 類_第5頁
已閱讀5頁,還剩45頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第7章類面向過程編程思想結(jié)構(gòu)化編程,是自頂向下逐步分解的設(shè)計過程程序=數(shù)據(jù)結(jié)構(gòu)+算法,更關(guān)注算法效率最小單元是函數(shù)不足之處:設(shè)計不夠直觀:設(shè)計過程需按功能劃分,且各個功能沒有主體概念,不利于大型軟件的設(shè)計適應(yīng)性較差:數(shù)據(jù)與數(shù)據(jù)操作邏輯相對分散,容易導(dǎo)致代碼間耦合性較高,從而導(dǎo)致代碼修改的級聯(lián)影響范圍較大,程序的健壯性和維護性不太友好,不太適合大型復(fù)雜系統(tǒng)設(shè)計面向?qū)ο缶幊趟枷雽ΜF(xiàn)實世界中的事物對象進行抽象描述,設(shè)計過程更關(guān)注各個對象的狀態(tài)屬性和功能行為,通過對象間協(xié)作完成功能最小單元是類類=成員變量(屬性數(shù)據(jù))+方法(行為)特點:通過封裝、繼承和多態(tài)機制構(gòu)建面向?qū)ο蟮能浖到y(tǒng)火車站購票案例火車站購票案例面向過程和面向?qū)ο竺嫦驅(qū)ο笈c面向過程:二者相輔相成,并不是對立的。解決復(fù)雜問題,通過面向?qū)ο蠓绞奖阌谖覀儚暮暧^上把握事物之間復(fù)雜的關(guān)系、方便我們分析整個系統(tǒng);具體到微觀操作,仍然使用面向過程方式來處理。類的定義在Python中一切皆對象,面向?qū)ο笸ㄟ^封裝、繼承、多態(tài)特性構(gòu)建一個面向?qū)ο蟮脑O(shè)計體系,通過對象之間的協(xié)作完成具體的業(yè)務(wù)邏輯和功能對象之間的協(xié)作存在兩個概念:服務(wù)提供者和服務(wù)使用者類的定義本質(zhì)上是一種封裝,是對某種事物的抽象,包括數(shù)據(jù)抽象和行為抽象,數(shù)據(jù)抽象是通過封裝抽象事物的狀態(tài)、數(shù)據(jù),行為抽象是指封裝操作數(shù)據(jù)的行為,從而隱藏對象內(nèi)部的復(fù)雜性,只對外公開簡單的服務(wù)訪問接口類定義了抽象事物的狀態(tài)(屬性)和行為(函數(shù)),對象則是類的具體實體,也可以稱為類的實例類的定義類定義與函數(shù)定義一樣,必須被執(zhí)行才會起作用,當(dāng)進入類定義時,將創(chuàng)建一個命名空間,并且該命名空間從屬于局部作用域,當(dāng)從結(jié)尾處正常離開類定義時,將創(chuàng)建一個類對象。類的聲明采用class保留字,語法形式如下:class<類名>: <類體>類的定義類對象支持兩種操作:屬性引用和實例化。屬性引用采用“.”運算符,即形如:。屬性引用中有效的屬性名稱是類對象被創(chuàng)建時存在于類命名空間中的所有名稱,包括類屬性和方法類的實例化使用函數(shù)表示法,可以把類對象當(dāng)作一個函數(shù),該函數(shù)無參數(shù),返回類型是一個該類的新實例。如實例化一個空的Goods對象形如:goods=Goods()類的定義實例化操作會創(chuàng)建一個空對象,如果需要創(chuàng)建帶有特定初始狀態(tài)的自定義實例,則通過定義一個名為__init__的特殊方法。當(dāng)一個類定義了__init__方法時,類的實例化操作會自動為新創(chuàng)建的類實例調(diào)用__init__方法,其中__init__可接受參數(shù)用于實例的初始化。類的實例化得到的實例對象支持屬性引用操作,有效的屬性名稱包括兩種:數(shù)據(jù)屬性和方法。數(shù)據(jù)屬性即為實例屬性,數(shù)據(jù)屬性不需要聲明,在第一次被賦值時產(chǎn)生。實例對象的有效方法名稱依賴于其所屬的類,一個類中,所有是函數(shù)對象的屬性都定義了其實例的對應(yīng)方法。實例屬性類定義中的屬性用于描述抽象事物的特征或者狀態(tài),分為實例屬性和類屬性實例屬性是指實例對象的屬性,同一個類的不同對象,其實例屬性是相互隔離的,獨屬于該實例對象實例屬性無需聲明,在賦值時創(chuàng)建,主要是在構(gòu)造方法__init__中定義,同時在定義和在實例方法中訪問該屬性時往往以self為前綴實例屬性也可以在非構(gòu)造方法中以self前綴進行定義實例屬性,即實例屬性的定義是在實例方法中在類的外部訪問這些實例屬性則需要首先創(chuàng)建類的實例對象,然后通過形如的形式進行訪問或者更新實例屬性示例定義一個超市中的普通商品類,該商品類具有商品名稱、商品價格、商品庫存和商品簡介:

classGoods:def__init__(self,name,price,count,desc):=nameself.price=priceself.count=countself.desc=descnotebook=Goods("筆記本",3.5,10,"100頁的晨光筆記本")print()="晨光筆記本"print()

類屬性類屬性是從屬于類的,為該類的所有對象進行共享該變量,其生命周期是在執(zhí)行了類的定義開始有效類屬性的定義不會在任何成員方法之內(nèi),一般定義在類的開頭,也可定義在類的中間,只要是在類的命名空間可訪問的地方即可在類的外部,類屬性可以通過類名或者類的實例對象引用訪問。類屬性示例定義個面包類,面包類一般有有效期屬性,該屬性是屬于一個實例對象的。而有效期屬性一般是datetime類型,且在構(gòu)造時一般是輸入的日期字符串,將日期字符串格式化為datetime對象的日期格式這一屬性應(yīng)為面包類的所有對象共享,所有的面包類統(tǒng)一采用一個格式化字符串,該屬性則應(yīng)定義為類屬性

classBread:format_str="%Y/%m/%d%H:%M:%S"def__init__(self,validDate):print(self.format_str)self.validDate=datetime.strptime(validDate,self.format_str)print(Bread.format_str)bread=Bread("2021/05/0417:57:0")print(bread.validDate)

屬性訪問約定Python不同于如Java等面向?qū)ο笳Z言,其沒有提供訪問權(quán)限控制符,即僅對象內(nèi)部能訪問的“私有”實例變量在Python中并不存在Python中的訪問權(quán)限控制采用的是通用約定形式,實際訪問控制取決于類的使用者,但是建議大家遵守該約定,便于代碼的維護。屬性訪問約定該約定根據(jù)名稱中下劃線前綴可分為如下四種情況,其中名稱包括變量和方法:如果名稱無下劃線前綴,則約定該變量或方法是公開訪問的,類的實現(xiàn)者即服務(wù)提供者會盡可能保證該部分穩(wěn)定不變;如果名稱前有一個下劃線,則約定該變量或方法應(yīng)視為非公有部分,屬于實現(xiàn)細(xì)節(jié),后續(xù)重構(gòu)優(yōu)化迭代可能不經(jīng)通知而發(fā)生改變;屬性訪問約定如果名稱前面有兩個下劃線,則約定該變量或方法應(yīng)視為私有成員,在類的外部不可直接訪問,該類型的成員在類定義的內(nèi)部將通過重命名形式在語法上提供支持,將__var成員按_className_var形式重命名該變量,故如果在類的外部直接訪問__var則會報錯示例:執(zhí)行print語句會報錯如下:AttributeError:typeobject'TestPrivate'hasnoattribute'__name'

classTestPrivate:__name="testprivate"print(TestPrivate.__name)

屬性訪問約定如果名稱前后均有兩個下劃線,則約定該部分為類的內(nèi)置屬性。如__name__查看類名等,部分內(nèi)置屬性如下:特殊屬性含義object.__dict__對象的屬性字典instance.__class__對象所屬的類class.__bases__類的基類元組class.__base__類的基類class.__name__類的名稱class.__qualname__類的限定名稱class.__mro__方法查找順序,基類元組class.mro()同上,可被子類重寫class.__subclasses__()子類列表property在一些業(yè)務(wù)場景中,我們希望在對實例屬性的賦值、更新或訪問進行控制,如執(zhí)行類型檢查、權(quán)限控制、值合法性驗證等,此時可以采用property裝飾器通過property裝飾器修飾的方法稱為property屬性,該屬性可以有三個互相關(guān)聯(lián)的方法,這三個關(guān)聯(lián)方法必須具有相同的名稱,分別通過采用@property、@func.setter、@func.deleter修飾,分別對應(yīng)屬性的獲取、設(shè)置、刪除,其中func值為@property修飾的方法名采用property修飾的方法可以像普通屬性的形式進行訪問,同時會根據(jù)訪問方式的不同自動觸發(fā)getter、setter和deleter方法。property示例商品的價格屬性為例,通過property進行類型檢查和值合法性驗證:

classGoods:def__init__(self,price):self.price=price@propertydefprice(self):print("executegetter")returnself.__price@price.setterdefprice(self,rawPrice):print("executesetter")ifnotisinstance(rawPrice,float)andnotisinstance(rawPrice,int):raiseTypeError("價格類型不合法")ifrawPrice<0:raiseValueError("價格不能小于0")self.__price=rawPrice@price.deleterdefprice(self):print("executedeleter")raiseAttributeError("商品對象不能刪除價格屬性")

notebook=Goods(3.5)print(notebook.price)notebook.price=4print(notebook.price)delnotebook.price執(zhí)行結(jié)果為:executesetterexecutegetter3.5executesetterexecutegetter4executedeleterTraceback(mostrecentcalllast):File"TestProperty.py",line28,in<module>delnotebook.priceFile"TestProperty.py",line22,inpriceraiseAttributeError("商品對象不能刪除價格屬性")AttributeError:商品對象不能刪除價格屬性

property示例對于已經(jīng)存在的getter和setter方法,同樣也可以將它們定義為property

classGoods:def__init__(self,price):self.set_price(price)defget_price(self):print("executegetter")returnself.__pricedefset_price(self,rawPrice):print("executesetter")ifnotisinstance(rawPrice,float)andnotisinstance(rawPrice,int):raiseTypeError("價格類型不合法")ifrawPrice<0:raiseValueError("價格不能小于0")self.__price=rawPricedefdel_price(self):print("executedeleter")raiseAttributeError("商品對象不能刪除價格屬性")

price=property(get_price,set_price,del_price)

property查看property的構(gòu)造方法可知,property屬性本質(zhì)上就是把getter、setter、deleter方法綁定到property自身所持有的fget、fset和fdel三個屬性中。property的構(gòu)造方法簽名如下所示:def__init__(self,fget=None,fset=None,fdel=None,doc=None)property也可以用于定義一些需要計算的屬性,該類屬性并不會實際保存起來,而是根據(jù)需要完成計算,從而使得可統(tǒng)一采用屬性形式訪問,而無需將屬性和方法調(diào)用混合使用property示例長方形計算面積和周長為例

classRectangle:def__init__(self,width,height):self.__width=widthself.__height=height@propertydefarea(self):returnself.__width*self.__height@propertydefperimeter(self):return2*(self.__width+self.__height)rect=Rectangle(2,3)print(rect.area)print(rect.perimeter)

方法類的定義封裝了對數(shù)據(jù)的操作邏輯或者是抽象事物的功能行為,這些在類定義中采用方法形式存在。類的方法分為:實例方法、靜態(tài)方法和類方法三種實例方法是指從屬于實例對象的方法,調(diào)用該方法需要實例化對象,該方法可以訪問實例屬性和類屬性一般實例方法聲明語法格式如下:def方法名(self,[形參列表]):

函數(shù)體實例方法調(diào)用格式如下:

對象.方法名([實參列表])實例方法示例商品類為例,商品類就有查看商品詳情和計算支付價格行為,而商品詳情設(shè)計的屬性和價格為實例屬性,即方法操作的是具體的實例對象,故商品類應(yīng)該具有查看詳情和支付價格計算兩個實例方法:

classGoods:def__init__(self,name,price,count,desc):self._name=nameself._price=priceself._count=countself._desc=descdefdetail(self):print("商品名稱:{}\n單價:{}元/件\n庫存:{}件\n商品描述:{}".format(self._name,self._price,self._count,self._desc))defgetPayPrice(self,amount):returnself._price*amountnotebook=Goods("筆記本",3.5,10,"100頁的筆記本")notebook.detail()print(notebook.getPayPrice(3))

靜態(tài)方法靜態(tài)方法是不依賴于類實例對象和類對象的方法,一般是跟類有一定關(guān)系的功能,但是在運行時又不需要實例對象和類對象參與,如一些與類相關(guān)的工具方法,雖然可以定義到類的外面,但是可能該工具方法只在該類中進行調(diào)用,即僅服務(wù)于該類,此時采用定義靜態(tài)方法形式將該方法包含在類的命名空間中,提高代碼內(nèi)聚。靜態(tài)方法采用@staticmethod裝飾,另因為靜態(tài)方法沒有接收實例對象和類對象的引用參數(shù),故無法訪問類的實例屬性和類屬性,類的實例和類對象均可調(diào)用靜態(tài)方法。類方法類方法是采用@classmethod裝飾的方法,其中的第一個參數(shù)為當(dāng)前調(diào)用類對象的引用,一般命名為cls,類方法操作的是類對象而非實例對象,故可操作類屬性,不可訪問實例屬性。如果需要在實例化類對象前提供某些功能,則需要采用類方法或者靜態(tài)方法類方法對于實現(xiàn)多種構(gòu)造方法提供了很好的幫助。類方法示例

classDate:def__init__(self,day=0,month=0,year=0):self.day=dayself.month=monthself.year=yeardefdisplay(self):return"{0}-{1}-{2}".format(self.year,self.month,self.day)@classmethoddeffrom_string(cls,date_as_string):year,month,day=map(int,date_as_string.split('-'))dateObj=cls(day,month,year)returndateObj@staticmethoddefis_date_valid(date_as_string):year,month,day=map(int,date_as_string.split('-'))returnday<=31andmonth<=12andyear<=3999

date=Date(2021,5,31)print(date)#1print(date.display)#2print(date.from_string)#3print(date.is_date_valid)#4dateObj=Date.from_string('2021-05-31’)#5print(dateObj)#6is_date=Date.is_date_valid('2021-05-31')print(is_date)執(zhí)行結(jié)果為:<__main__.Dateobjectat0x000002038392F438><boundmethodDate.displayof<__main__.Dateobjectat0x000002038392F438>><boundmethodDate.from_stringof<class'__main__.Date’>><functionDate.is_date_validat0x0000020383923D90><__main__.Dateobjectat0x000002038392F550>True

構(gòu)造方法示例類通過__init__方法實例化對象,__init__方法的第一個參數(shù)是類的實例對象,那么傳遞給__init__的實例對象是如何得到的呢?在Python中實例對象的創(chuàng)建分為兩步:首先是創(chuàng)建對象,由__new__()負(fù)責(zé)對象的創(chuàng)建然后是初始化對象,由

__init__方法負(fù)責(zé)對象的初始化

classTest:def__init__(self):print("__init__")print(self)def__new__(cls):print("__new__")self=super(Test,cls).__new__(cls)print(self)returnselftest=Test()

__new__<__main__.Testobjectat0x0000021022395D30>__init__<__main__.Testobjectat0x0000021022395D30>

構(gòu)造方法__new__方法先于__init__()方法執(zhí)行,且__init__中的self就是__new__方法返回的,即__new__方法的返回值就是類的實例對象,這個實例對象會傳遞給__init__方法中定義的self參數(shù),用于后續(xù)對該實例對象進行正確初始化。如果__new__方法不返回值或者返回None,那么__init__將不會得到調(diào)用,因為實例對象沒有創(chuàng)建出來,自然也無需初始化。

__new__方法是用來控制對象的創(chuàng)建,如對象創(chuàng)建前后需要做的一些行為,也可以用于單例模式這種控制對象創(chuàng)建的過程__new__實現(xiàn)單例模式示例

classSingletonTest:_singleton=Nonedef__new__(cls):ifnotcls._singleton:cls._singleton=object.__new__(cls)returncls._singletonobj1=SingletonTest()obj2=SingletonTest()print(obj1)print(obj2)

<__main__.SingletonTestobjectat0x000001D8B4255C88><__main__.SingletonTestobjectat0x000001D8B4255C88>

析構(gòu)方法對象有創(chuàng)建,自然也有刪除回收資源,在python中執(zhí)行對象刪除的方法是__del__()方法,稱為析構(gòu)方法,與__init__()方法類似,原型為:def__del__(self):析構(gòu)方法一般有兩種調(diào)用時機:當(dāng)對象在某個作用域中調(diào)用完成,沒有其他地方引用時會調(diào)用析構(gòu)方法回收資源釋放內(nèi)存空間采用del指令刪除特定對象,此時如果該對象的引用次數(shù)為0,則會調(diào)用析構(gòu)方法回收資源析構(gòu)方法示例

classDestructorTest:def__init__(self):print("初始化對象")def__del__(self):print("刪除對象")obj=DestructorTest()delobjprint("---end---")

初始化對象刪除對象---end---

繼承繼承是面向?qū)ο笕筇匦灾?,繼承機制源于現(xiàn)實世界中許多對象之間具有共性和派生關(guān)系,通過繼承機制可解決代碼復(fù)用和維護代碼的一致性。繼承是子類繼承父類共性的東西,包括特征和行為,使得子類對象具有父類的屬性和方法,即在子類中可訪問父類的屬性和調(diào)用父類的方法。通過繼承創(chuàng)建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。python支持多重繼承,即一個子類可以繼承多個父類。繼承子類的定義格式如下:classDerive(基類1,基類2….):

類體示例:面包商品繼承普通商品,見Pycharm,繼承Python支持繼承且支持多繼承,故子類訪問的方法可能定義在當(dāng)前類,也可能來自于不同的基類,故在調(diào)用方法時需要對當(dāng)前類和基類進行搜索以確定方法所在的位置。搜索查找方法的順序稱為方法解析順序(methodresolutionorder,MRO)在Python3中,如類A可通過A.mro()或A.__mro__查看類A的MRO信息。Python提供了多種MRO,其中Python3采用的C3算法得到的MRO,用于保證繼承關(guān)系中線性化的單調(diào)性原則和局部優(yōu)先級。C3算法首先把類C的MRO記為L[C]=[C1,C2,…,CN],其中C1稱為L[C]的頭,其余元素[C2,…,CN]稱為尾如果一個類C繼承自基類B1,B2,…,BN,即classC(B1,B2,…BN),則L[C]通過以下兩步計算生成:L[object]=[object]L[C(B1…BN)]=C+merge(L[B1],L[B2],…L[BN],B1,B2,…BN)說明:B1B2…BN表示[B1,B2,…BN];[C]+[B1,B2,...,BN]=[C,B1,B2,…BN];C3算法merge操作為C3算法核心,具體流程為:檢查第一個列表的頭元素(如L[B1]的頭),記為H;若H未出現(xiàn)在其他列表的尾部,則將其輸出,并將其從所有列表中刪除,然后回到步驟a,否則取出下一個列表的頭部記為H,繼續(xù)該步驟;重復(fù)上述步驟,直到列表為空或者不能再找出可輸出的元素,如果是前一種情況,則算法結(jié)束,如果是后一種情況,則說明無法構(gòu)建繼承關(guān)系,Python將拒絕創(chuàng)建C類并拋出異常。C3算法示例

classX:passclassY:passclassA(X,Y):passclassB(Y,X):passclassC(A,B):pass

C3算法示例其中類C的MRO計算過程如下:1.L[object]=[object];L[X]=[X,object];L[Y]=[Y,object]2.L[A]=[A]+merge(L[X],L[Y],[X],[Y])

=[A]+merge([X,object],[Y,object],[X],[Y])=[A,X]+merge([object],[Y,object],[Y])=[A,X,Y]+merge([object],[object])=[A,X,Y,object]C3算法示例同理得到L[B]=[B,Y,X,object];最后計算類C的MRO:L[C]=[C]+merge(L[A],L[B],[A],[B])=[C]+merge([A,X,Y,object],[B,Y,X,object],[A],[B])=[C,A]+merge([X,Y,object],[B,Y,X,object],[B])=[C,A,B]+merge([X,Y,object],[Y,X,object])此時merge操作中無法找到新的輸出元素,因為第一個列表[X,Y,object]的頭元素X出現(xiàn)在第二個列表[Y,X,object]的尾部,而第二個列表的頭元素Y出現(xiàn)在第一個類別的尾部,故沒有符合條件的元素可輸出繼續(xù)迭代,此時Python會拒絕創(chuàng)建類C并拋出異常方法重寫子類繼承了父類的方法,但是子類有其自己的差異性,有些行為不同于父類,此時需要重寫父類的方法,如前面Bread類detail方法,因為Bread類多了有效期屬性validDate,故查看商品詳情的時候需要對detail方法重寫。因為Python中不存在方法重載,多個同名的方法,則以最后一個為準(zhǔn),故子類如果有與父類同名的方法,根據(jù)MRO可知,首先查找子類,在子類中找到了同名方法名,則搜索停止不再到父類搜索,調(diào)用時如果參數(shù)列表不符合則會報錯。方法重寫在子類想要調(diào)用父類方法時,可直接采用BaseClassName.method()的方式進行調(diào)用,也可以采用super().method()的方法調(diào)用這兩種方式在Python單繼承時效果一致,多繼承時super與父類沒有直接關(guān)聯(lián),而是獲取繼承順序中的下一個類,在多繼承時,方法的查找順序參考MRO順序,可通過ClassName.__mro__查看該順序示例:見Pycharmsupersuper是一個類,而非關(guān)鍵字和函數(shù),當(dāng)我們調(diào)用super()時,會實例化一個super類得到一個super實例,該

溫馨提示

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

最新文檔

評論

0/150

提交評論