(第七集——第一章)python面向对象
程序設(shè)計(jì)分類
面向過程
面向過程好比精心設(shè)計(jì)好一條流水線,是一種機(jī)械式的思維方式。
# 優(yōu)點(diǎn): 復(fù)雜度的問題流程化,進(jìn)而簡(jiǎn)單化(一個(gè)復(fù)雜的問題,分成一個(gè)個(gè)小的步驟去實(shí)現(xiàn),實(shí)現(xiàn)小的步驟將會(huì)非常簡(jiǎn)單) # 缺點(diǎn): 一套流水線或者流程就是用來(lái)解決一個(gè)問題,生產(chǎn)汽水的流水線無(wú)法生產(chǎn)汽車,即便是能,也得是大改,改一個(gè)組件,牽一發(fā)而動(dòng)全身。# 應(yīng)用場(chǎng)景: 一旦完成很少改變的場(chǎng)景,例子有Linux內(nèi)核,git,以及Apache HTTP Server等面向?qū)ο?/p>
對(duì)象是特征與技能的結(jié)合體,面向?qū)ο蟾幼⒅貙?duì)現(xiàn)實(shí)世界的模擬,
優(yōu)點(diǎn):
程序的擴(kuò)展性強(qiáng)。對(duì)某一個(gè)對(duì)象單獨(dú)修改,會(huì)立刻反映到整個(gè)體系中,如對(duì)游戲中一個(gè)人物參數(shù)的特征和技能修改都很容易。
缺點(diǎn):
編程的復(fù)雜度遠(yuǎn)高于面向過程
無(wú)法像面向過程的程序設(shè)計(jì)流水線式的可以很精準(zhǔn)的預(yù)測(cè)問題的處理流程與結(jié)果.
應(yīng)用場(chǎng)景:需求經(jīng)常變化的軟件,一般需求的變化都集中在用戶層,互聯(lián)網(wǎng)應(yīng)用,企業(yè)內(nèi)部軟件,游戲等都是面向?qū)ο蟮某绦蛟O(shè)計(jì)大顯身手的好地方.
類與對(duì)象
類即類別、種類,是面向?qū)ο笤O(shè)計(jì)最重要的概念,對(duì)象是特征與技能的結(jié)合體,類是一系列對(duì)象相似的特征與技能的結(jié)合體。
類就是將數(shù)據(jù)與操作數(shù)據(jù)的方法組合到一起,實(shí)現(xiàn)數(shù)據(jù)與功能是統(tǒng)一.
屬性查找
類的屬性
# 數(shù)據(jù)屬性:類的數(shù)據(jù)屬性是所有對(duì)象共享的 print(id(s1.school)) # 4377347328 print(id(s2.school)) # 4377347328# id是python的實(shí)現(xiàn)機(jī)制,并不能真實(shí)反映內(nèi)存地址, 如果有內(nèi)存地址,還是以內(nèi)存地址為準(zhǔn) # 函數(shù)屬性:類的函數(shù)屬性是綁定給對(duì)象用的 print(OldboyStudent.learn) # <function OldboyStudent.learn at 0x1021329d8> print(s1.learn) # <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x1021466d8>> print(s2.learn) # <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146710>> # 類的函數(shù)屬性是綁定給對(duì)象使用的,obj.method稱為綁定方法,內(nèi)存地址都不一樣,類的函數(shù)屬性是綁定給對(duì)象使用的備注:在obj.name會(huì)先從obj自己的名稱空間里找name,找不到則去類中找,類也找不到就找父類…最后都找不到就拋出異常
綁定到對(duì)象的方法的特殊之處
#改寫 class OldboyStudent:school='oldboy'def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexdef learn(self):print('%s is learning' %self.name) #新增self.namedef eat(self):print('%s is eating' %self.name)def sleep(self):print('%s is sleeping' %self.name)s1=OldboyStudent('李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴彈','男',78) 類中定義的函數(shù)(沒有被任何裝飾器裝飾的)是類的函數(shù)屬性, 類可以使用,但必須遵循函數(shù)的參數(shù)規(guī)則,有幾個(gè)參數(shù)需要傳幾個(gè)參數(shù): OldboyStudent.learn(s1) #李坦克 is learning OldboyStudent.learn(s2) #王大炮 is learning OldboyStudent.learn(s3) #牛榴彈 is learning 類中定義的函數(shù)(沒有被任何裝飾器裝飾的), 其實(shí)主要是給對(duì)象使用的,而且是綁定到對(duì)象的, 雖然所有對(duì)象指向的都是相同的功能,但是綁定到不同的對(duì)象就是不同的綁定方法強(qiáng)調(diào):綁定到對(duì)象的方法的特殊之處在于,綁定給誰(shuí)就由誰(shuí)來(lái)調(diào)用, 誰(shuí)來(lái)調(diào)用,就會(huì)將‘誰(shuí)’本身當(dāng)做第一個(gè)參數(shù)傳給方法,即自動(dòng)傳值(方法__init__也是一樣的道理) s1.learn() #等同于OldboyStudent.learn(s1) s2.learn() #等同于OldboyStudent.learn(s2) s3.learn() #等同于OldboyStudent.learn(s3)注意:綁定到對(duì)象的方法的這種自動(dòng)傳值的特征,決定了在類中定義的函數(shù)都要默認(rèn)寫一個(gè)參數(shù)self,self可以是任意名字,但是約定俗成地寫出self 類即類型:提示:python的class術(shù)語(yǔ)與c++有一定區(qū)別,與 Modula-3更像。python中一切皆為對(duì)象,且python3中類與類型是一個(gè)概念,類型就是類.#類型dict就是類dict >>> list <class 'list'>#實(shí)例化的到3個(gè)對(duì)象l1,l2,l3 >>> l1=list() >>> l2=list() >>> l3=list()#三個(gè)對(duì)象都有綁定方法append,是相同的功能,但內(nèi)存地址不同 >>> l1.append <built-in method append of list object at 0x10b482b48> >>> l2.append <built-in method append of list object at 0x10b482b88> >>> l3.append <built-in method append of list object at 0x10b482bc8>#操作綁定方法l1.append(3),就是在往l1添加3,絕對(duì)不會(huì)將3添加到l2或l3 >>> l1.append(3) >>> l1 [3] >>> l2 [] >>> l3 []#調(diào)用類list.append(l3,111)等同于l3.append(111) >>> list.append(l3,111) #l3.append(111) >>> l3 [111]
對(duì)象之間的交互
class Garen: #定義英雄蓋倫的類,不同的玩家可以用它實(shí)例出自己英雄;camp='Demacia' #所有玩家的英雄(蓋倫)的陣營(yíng)都是Demacia;def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻擊力58...;self.nickname=nickname #為自己的蓋倫起個(gè)別名;self.aggressivity=aggressivity #英雄都有自己的攻擊力;self.life_value=life_value #英雄都有自己的生命值;def attack(self,enemy): #普通攻擊技能,enemy是敵人;enemy.life_value-=self.aggressivity #根據(jù)自己的攻擊力,攻擊敵人就減掉敵人的生命值。 class Riven:camp='Noxus' #所有玩家的英雄(銳雯)的陣營(yíng)都是Noxus;def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻擊力54;self.nickname=nickname #為自己的銳雯起個(gè)別名;self.aggressivity=aggressivity #英雄都有自己的攻擊力;self.life_value=life_value #英雄都有自己的生命值;def attack(self,enemy): #普通攻擊技能,enemy是敵人;enemy.life_value-=self.aggressivity #根據(jù)自己的攻擊力,攻擊敵人就減掉敵人的生命值。
繼承與派生
繼承是一種創(chuàng)建新類的方式,新建的類可以繼承一個(gè)或多個(gè)父類(python支持多繼承),父類又可稱為基類或超類,新建的類稱為派生類或子類。子類會(huì)“”遺傳”父類的屬性,從而解決代碼重用問題。
python中類的繼承分為:單繼承和多繼承 class ParentClass1: #定義父類passclass ParentClass2: #定義父類passclass SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClasspassclass SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號(hào)分隔開多個(gè)繼承的類pass 查看繼承: >>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個(gè)子類,__bases__則是查看所有繼承的父類 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) 經(jīng)典類與新式類: 只有在python2中才分新式類和經(jīng)典類,python3中統(tǒng)一都是新式類 在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經(jīng)典類 在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類 在python3中,無(wú)論是否繼承object,都默認(rèn)繼承object,即python3中所有類均為新式類 如: >>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)繼承與抽象(先抽象再繼承)
抽象: 繼承描述的是子類與父類之間的關(guān)系,是一種什么是什么的關(guān)系。要找出這種關(guān)系,必須先抽象再繼承 抽象分成兩個(gè)層次:1.將奧巴馬和梅西這倆對(duì)象比較像的部分抽取成類; 2.將人,豬,狗這三個(gè)類比較像的部分抽取成父類。 (抽象最主要的作用是劃分類別) 繼承: 基于抽象的結(jié)果,通過編程語(yǔ)言去實(shí)現(xiàn)它,是先經(jīng)歷抽象這個(gè)過程,才能通過繼承的方式去表達(dá)出抽象的結(jié)構(gòu)。 抽象只是分析和設(shè)計(jì)的過程中,一個(gè)動(dòng)作或者說(shuō)一種技巧,通過抽象可以得到類。繼承與重用性
在開發(fā)程序的過程中,如果我們定義了一個(gè)類A,然后又新建立另外一個(gè)類B,但類B的大部分內(nèi)容與類A的相同時(shí),可以不用重新寫一個(gè)類B,采用類的繼承的概念。 通過繼承的方式新建類B,讓B繼承A,B會(huì)‘遺傳’A的所有屬性(數(shù)據(jù)屬性和函數(shù)屬性),實(shí)現(xiàn)代碼重用 class Hero:def __init__(self,nickname,aggressivity,life_value):self.nickname=nicknameself.aggressivity=aggressivityself.life_value=life_valuedef move_forward(self): print('%s move forward' %self.nickname)def move_backward(self):print('%s move backward' %self.nickname)def move_left(self):print('%s move forward' %self.nickname)def move_right(self):print('%s move forward' %self.nickname)def attack(self,enemy):enemy.life_value-=self.aggressivityclass Garen(Hero): # 繼承Hero類passclass Riven(Hero): # 繼承Hero類passg1=Garen('草叢倫',100,300) r1=Riven('銳雯雯',57,200)print(g1.life_value) r1.attack(g1) # 調(diào)用父類中的方法 print(g1.life_value)''' 運(yùn)行結(jié)果 243 ''' class Foo:def f1(self):print('Foo.f1')def f2(self):print('Foo.f2')self.f1() # 因?yàn)閟elf表示子類Bar的對(duì)象,所以會(huì)調(diào)用子類Bar類中的f1函數(shù)class Bar(Foo):def f1(self):print('Foo.f1')b=Bar() b.f2() ''' 打印結(jié)果: Foo.f2 Foo.f1 '''
派生
當(dāng)然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會(huì)影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調(diào)用新增的屬性時(shí),就以自己為準(zhǔn)了。 class Riven(Hero):camp='Noxus'def attack(self,enemy): #在子類中定義新的attack,不再使用父類的attack,不會(huì)影響父類print('from riven')def fly(self): #在子類中定義新的函數(shù)print('%s is flying' %self.nickname) 在子類中,新建的重名的函數(shù)屬性,在編輯函數(shù)內(nèi)功能的時(shí)候,有可能需要重用父類中重名的那個(gè)函數(shù)功能, 應(yīng)該是用調(diào)用普通函數(shù)的方式,即:類名.func(),此時(shí)就與調(diào)用普通函數(shù)無(wú)異了,因此即便是self參數(shù)也要為其傳值class Riven(Hero):camp='Noxus'def __init__(self,nickname,aggressivity,life_value,skin):Hero.__init__(self,nickname,aggressivity,life_value) #調(diào)用父類功能self.skin=skin #新屬性def attack(self,enemy): #在自己這里定義新的attack,不再使用父類的attack,且不會(huì)影響父類Hero.attack(self,enemy) #調(diào)用功能print('from riven')def fly(self): #在自己這里定義新的print('%s is flying' %self.nickname)r1=Riven('銳雯雯',57,200,'比基尼') r1.fly() print(r1.skin)''' 運(yùn)行結(jié)果 銳雯雯 is flying 比基尼'''
組合與重用性
軟件重用除繼承外還有組合,指的是在一個(gè)類中以另外一個(gè)類的對(duì)象作為數(shù)據(jù)屬性,稱為類的組合:>>> class Equip: #武器裝備類 ... def fire(self): ... print('release Fire skill') ... >>> class Riven: #英雄Riven的類,一個(gè)英雄需要有裝備,因而需要組合Equip類 ... camp='Noxus' ... def __init__(self,nickname): ... self.nickname=nickname ... self.equip=Equip() #用Equip類產(chǎn)生一個(gè)裝備,賦值給實(shí)例的equip屬性 ... >>> r1=Riven('銳雯雯') >>> r1.equip.fire() #可以使用組合的類產(chǎn)生的對(duì)象所持有的方法 release Fire skill 組合與繼承都是有效地利用已有類的資源的方式。但是二者的概念和使用場(chǎng)景不同:1.繼承的方式: 通過繼承建立了派生類與基類之間的關(guān)系,它是一種'是'的關(guān)系,比如白馬是馬,人是動(dòng)物。 當(dāng)類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學(xué)生是人2.組合的方式: 用組合的方式建立了類與組合的類之間的關(guān)系,它是一種‘有’的關(guān)系,比如教授有生日, 教授教python和linux課程,教授有學(xué)生s1、s2、s3...當(dāng)類之間有顯著不同,并且較小的類是較大的類所需要的組件時(shí),用組合比較好: class People:def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexclass Course:def __init__(self,name,period,price):self.name=nameself.period=periodself.price=pricedef tell_info(self):print('<%s %s %s>' %(self.name,self.period,self.price))class Teacher(People):def __init__(self,name,age,sex,job_title):People.__init__(self,name,age,sex)self.job_title=job_titleself.course=[]self.students=[]class Student(People):def __init__(self,name,age,sex):People.__init__(self,name,age,sex)self.course=[]t1=Teacher('egon',18,'male','沙河霸道金牌講師') s1=Student('牛榴彈',18,'female')python=Course('python','3mons',3000.0) linux=Course('linux','3mons',3000.0)#為老師egon和學(xué)生s1添加課程 t1.course.append(python) # 將python對(duì)象添加到老師 course列表屬性中 t1.course.append(linux) # 將linux對(duì)象添加到 老師course列表屬性中 s1.course.append(python) # 將python對(duì)象添加到 學(xué)生course列表屬性中#為老師egon添加學(xué)生s1 t1.students.append(s1)#使用 for obj in t1.course: # t1.course屬性保存的是Cource類的對(duì)象obj.tell_info() # obj此時(shí)代表的是Cource類的對(duì)象,"""結(jié)果:<python 3mons 3000.0><linux 3mons 3000.0>"""接口與歸一化設(shè)計(jì)
接口提取了一群類共同的函數(shù),可以把接口當(dāng)做一個(gè)函數(shù)的集合。然后讓子類去實(shí)現(xiàn)接口中的函數(shù)。
歸一化設(shè)計(jì):
只要是基于同一個(gè)接口實(shí)現(xiàn)的類,所有這些類產(chǎn)生的對(duì)象在使用時(shí),在用法上都一樣。好處:
1、讓使用者無(wú)需關(guān)心對(duì)象的類是什么,只要知道這些對(duì)象都具備某些功能,降低使用者的使用難度。
2、 歸一化使得高層的外部使用者可以不加區(qū)分的處理所有接口兼容的對(duì)象集合。
(如:有一個(gè)汽車接口,里面定義了汽車所有的功能,然后由本田汽車的類,奧迪汽車的類,大眾汽車的類,
他們都實(shí)現(xiàn)了汽車接口,這樣就好辦了,大家只需要學(xué)會(huì)了怎么開汽車,那么無(wú)論是本田,還是奧迪,
還是大眾我們都會(huì)開了,開的時(shí)候根本無(wú)需關(guān)心我開的是哪一類車,操作手法(函數(shù)調(diào)用)都一樣)模仿interface接口
抽象類
python模仿Interface代碼只是看起來(lái)像接口,并未起到接口的作用,子類完全可不實(shí)現(xiàn)接口 ,這就用到了抽象類
抽象類:
抽象類是一個(gè)特殊的類,只能被繼承,不能被實(shí)例化,采用內(nèi)置模塊實(shí)現(xiàn)
類是從一堆對(duì)象中抽取相同的內(nèi)容而來(lái)的,那么抽象類就是從一堆類中抽取相同的內(nèi)容而來(lái)的,內(nèi)容包括數(shù)據(jù)屬性和函數(shù)屬性。
從設(shè)計(jì)角度去看,如果類是從現(xiàn)實(shí)對(duì)象抽象而來(lái)的,抽象類就是基于類抽象而來(lái)的。
從實(shí)現(xiàn)角度來(lái)看,抽象類與普通類的不同之處在于:抽象類中只能有抽象方法(沒有實(shí)現(xiàn)功能),該類不能被實(shí)例化,只能被繼承,且子類必須實(shí)現(xiàn)抽象方法.
抽象類與接口:
抽象類的本質(zhì)還是類,指的是一組類的相似性,包括數(shù)據(jù)屬性(如all_type)和函數(shù)屬性(如read、write),而接口只強(qiáng)調(diào)函數(shù)屬性的相似性。抽象類是一個(gè)介于類和接口之間的概念,同時(shí)具備類和接口的部分特性,可以用來(lái)實(shí)現(xiàn)歸一化設(shè)計(jì).
繼承實(shí)現(xiàn)的原理
Python中子類可以同時(shí)繼承多個(gè)父類,如A(B,C,D),
如果繼承關(guān)系為菱形結(jié)構(gòu),那么屬性的查找方式有兩種,分別是:深度優(yōu)先和廣度優(yōu)先
python繼承原理
python對(duì)于自定義的每一個(gè)類,python會(huì)計(jì)算出一個(gè)方法解析順序(MRO)列表, MRO列表就是一個(gè)簡(jiǎn)單的所有基類的線性順序列表:F.mro() # 等同于F.__mro_,查看繼承順序_ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]實(shí)現(xiàn)繼承時(shí),python會(huì)在MRO列表上從左到右開始查找基類,直到找到第一個(gè)匹配這個(gè)屬性的類為止。 而這個(gè)MRO列表的構(gòu)造是通過一個(gè)C3線性化算法實(shí)現(xiàn),它實(shí)際就是合并所有父類的MRO列表,并遵循如下三條準(zhǔn)則: 1.子類會(huì)先于父類被檢查 2.多個(gè)父類會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查 3.如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類子類中調(diào)用父類的方法
方法一:指名道姓,即父類名.父類方法() #_*_coding:utf-8_*_ __author__ = 'Linhaifeng'class Vehicle: #定義交通工具類Country='China'def __init__(self,name,speed,load,power):self.name=nameself.speed=speedself.load=loadself.power=powerdef run(self):print('開動(dòng)啦...')class Subway(Vehicle): #地鐵def __init__(self,name,speed,load,power,line):Vehicle.__init__(self,name,speed,load,power) # 初始化父類中的構(gòu)造方法,為父類中的內(nèi)存提供參數(shù)self.line=linedef run(self):print('地鐵%s號(hào)線歡迎您' %self.line)Vehicle.run(self) # 調(diào)用父類中的run方法,此時(shí)不會(huì)報(bào)錯(cuò),因?yàn)樯厦嬲{(diào)用了父類中的構(gòu)造方法,內(nèi)存中有該類,傳入的參數(shù)為Subway對(duì)象line13=Subway('中國(guó)地鐵','180m/s','1000人/箱','電',13) line13.run() # 調(diào)用子類中的run方法,該方法中包含調(diào)用父類中的方法,所以最終會(huì)調(diào)用父類中的方法 方法二:super():class Vehicle: #定義交通工具類Country='China'def __init__(self,name,speed,load,power):self.name=nameself.speed=speedself.load=loadself.power=powerdef run(self):print('開動(dòng)啦...')class Subway(Vehicle): #地鐵def __init__(self,name,speed,load,power,line):#super(Subway,self) 就相當(dāng)于實(shí)例本身 在python3中super()等同于super(Subway,self)super().__init__(name,speed,load,power) # 初始化父類中的方法self.line=linedef run(self):print('地鐵%s號(hào)線歡迎您' %self.line)super(Subway,self).run() # 調(diào)用父類中的方法,將Subway的對(duì)象作為參數(shù)傳入class Mobike(Vehicle):#摩拜單車passline13=Subway('中國(guó)地鐵','180m/s','1000人/箱','電',13) line13.run() # 兩者區(qū)別: 強(qiáng)調(diào):二者使用哪一種都可以,但最好不要混合使用 1、即使沒有直接繼承關(guān)系,super仍然會(huì)按照mro繼續(xù)往后查找 #指名道姓:父類名.父類方法() class A:def __init__(self):print('A的構(gòu)造方法') class B(A):def __init__(self):print('B的構(gòu)造方法')A.__init__(self)class C(A):def __init__(self):print('C的構(gòu)造方法')A.__init__(self)class D(B,C):def __init__(self):print('D的構(gòu)造方法')B.__init__(self)C.__init__(self)pass f1=D() #A.__init__被重復(fù)調(diào)用 ''' D的構(gòu)造方法 B的構(gòu)造方法 A的構(gòu)造方法 C的構(gòu)造方法 A的構(gòu)造方法 '''#使用super() class A:def __init__(self):print('A的構(gòu)造方法') class B(A):def __init__(self):print('B的構(gòu)造方法')super(B,self).__init__()class C(A):def __init__(self):print('C的構(gòu)造方法')super(C,self).__init__()class D(B,C):def __init__(self):print('D的構(gòu)造方法')super(D,self).__init__()f1=D() #super()會(huì)基于mro列表,往后找 ''' D的構(gòu)造方法 B的構(gòu)造方法 C的構(gòu)造方法 A的構(gòu)造方法 '''總結(jié): 當(dāng)使用super()函數(shù)時(shí),Python會(huì)在MRO列表上繼續(xù)搜索下一個(gè)類。 只要每個(gè)重定義的方法統(tǒng)一使用super()并只調(diào)用它一次,那么控制流最終會(huì)遍歷完整個(gè)MRO列表,每個(gè)方法也只會(huì)被調(diào)用一次 (注意注意注意:使用super調(diào)用的所有屬性,都是從MRO列表當(dāng)前的位置往后找,千萬(wàn)不要通過看代碼去找繼承關(guān)系,一定要看MRO列表)多態(tài)與多態(tài)性
多態(tài)指的是一類事物有多種形態(tài)
動(dòng)物有多種形態(tài):人,狗,豬:import abc class Animal(metaclass=abc.ABCMeta): #同一類事物:動(dòng)物@abc.abstractmethoddef talk(self): # 抽象類方法定義passclass People(Animal): #動(dòng)物的形態(tài)之一:人def talk(self):print('say hello')class Dog(Animal): #動(dòng)物的形態(tài)之二:狗def talk(self):print('say wangwang')class Pig(Animal): #動(dòng)物的形態(tài)之三:豬def talk(self):print('say aoao') 文件有多種形態(tài):文本文件,可執(zhí)行文件:import abc class File(metaclass=abc.ABCMeta): #同一類事物:文件@abc.abstractmethoddef click(self):passclass Text(File): #文件的形態(tài)之一:文本文件def click(self):print('open file')class ExeFile(File): #文件的形態(tài)之二:可執(zhí)行文件def click(self):print('execute file')多態(tài)性是指在不考慮實(shí)例類型的情況下使用實(shí)例:
向不同的對(duì)象發(fā)送同一條消息,不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為(即方法)。
多態(tài)性分為靜態(tài)多態(tài)性和動(dòng)態(tài)多態(tài)性:
靜態(tài)多態(tài)性:如任何類型都可以用運(yùn)算符+進(jìn)行運(yùn)算
動(dòng)態(tài)多態(tài)性:如下
封裝
python中用雙下劃線開頭的方式將屬性隱藏起來(lái)(設(shè)置成私有的):#其實(shí)這僅僅這是一種變形操作且僅僅只在類定義階段發(fā)生變形 #類中所有雙下劃線開頭的名稱如__x都會(huì)在類定義時(shí)自動(dòng)變形成:_類名__x的形式:class A:__N=0 #類的數(shù)據(jù)屬性就應(yīng)該是共享的,但是語(yǔ)法上是可以把類的數(shù)據(jù)屬性設(shè)置成私有的如__N,會(huì)變形為_A__Ndef __init__(self):self.__X=10 #變形為self._A__Xdef __foo(self): #變形為_A__fooprint('from A')def bar(self):self.__foo() #只有在類內(nèi)部才可以通過__foo的形式訪問到.# A._A__N是可以訪問到的, # 但是在外部是無(wú)法通過__x這個(gè)名字訪問到。 注意:1.這種機(jī)制也并沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問了,如a._A__N,即這種操作并不是嚴(yán)格意義上的限制外部訪問,僅僅只是一種語(yǔ)法意義上的變形,主要用來(lái)限制外部的直接訪問。 2.變形的過程只在類的定義時(shí)發(fā)生一次,在定義后的賦值操作,不會(huì)變形注意:1.python的封裝機(jī)制并沒有真正限制從外部直接訪問屬性, 當(dāng)知道類名和屬性名就可拼出名字:_類名__屬性進(jìn)行訪問, 如a._A__N,該操作并非嚴(yán)格意義上的限制外部訪問,只是一種語(yǔ)法意義上的變形,主要用來(lái)限制外部的直接訪問。 2.變形的過程只在類的定義時(shí)發(fā)生1次,在定義后的賦值操作,不會(huì)變形 3.在繼承中,父類如果不想讓子類重寫自己的方法,可將方法定義為私有#正常情況 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B#把fa定義成私有的,即__fa>>> class A: ... def __fa(self): #在定義時(shí)就變形為_A__fa ... print('from A') ... def test(self): ... self.__fa() #只有通過在自己類中對(duì)__fa()方法進(jìn)行調(diào)用,即調(diào)用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() # 調(diào)用父類中的test方法中封裝的__fa()方法,傳入的參數(shù)為b對(duì)象 from A
封裝不是單純意義的隱藏
1:封裝數(shù)據(jù):將數(shù)據(jù)隱藏不是目的。隱藏后對(duì)外提供操作該數(shù)據(jù)的接口,可在接口附加上對(duì)該數(shù)據(jù)操作的限制, 以此完成對(duì)數(shù)據(jù)屬性操作的嚴(yán)格控制: class Teacher:def __init__(self,name,age):# self.__name=name # 封裝屬性# self.__age=age # 封裝屬性self.set_info(name,age)def tell_info(self):print('姓名:%s,年齡:%s' %(self.__name,self.__age))def set_info(self,name,age): # 對(duì)傳入的參數(shù)進(jìn)行驗(yàn)證if not isinstance(name,str): # 類型判斷raise TypeError('姓名必須是字符串類型') # 拋出異常if not isinstance(age,int):raise TypeError('年齡必須是整型')self.__name=name # 驗(yàn)證通過,對(duì)封裝的屬性進(jìn)行賦值self.__age=aget=Teacher('egon',18) t.tell_info()t.set_info('egon',19) t.tell_info() # 2:封裝方法:目的是隔離復(fù)雜度封裝方法舉例: 1. 電視機(jī)本身是一個(gè)黑盒子,隱藏了所有細(xì)節(jié),但是一定會(huì)對(duì)外提供了一堆按鈕,這些按鈕也正是接口的概念,所以說(shuō),封裝并不是單純意義的隱藏!!!2. 快門就是傻瓜相機(jī)為傻瓜們提供的方法,該方法將內(nèi)部復(fù)雜的照相功能都隱藏起來(lái)了提示:在編程語(yǔ)言里,對(duì)外提供的接口(接口可理解為了一個(gè)入口),可以是函數(shù),稱為接口函數(shù),這與接口的概念還不一樣,接口代表一組接口函數(shù)的集合體。#取款是功能,而這個(gè)功能有很多功能組成:插卡、密碼認(rèn)證、輸入金額、打印賬單、取錢 #對(duì)使用者來(lái)說(shuō),只需要知道取款這個(gè)功能即可,其余功能我們都可以隱藏起來(lái),很明顯這么做 #隔離了復(fù)雜度,同時(shí)也提升了安全性class ATM:def __card(self):print('插卡')def __auth(self):print('用戶認(rèn)證')def __input(self):print('輸入取款金額')def __print_bill(self):print('打印賬單')def __take_money(self):print('取款')def withdraw(self):self.__card() # 調(diào)用在同一類中封裝的方法self.__auth()self.__input()self.__print_bill()self.__take_money()a=ATM() a.withdraw() 3: 了解 python并不會(huì)真的阻止訪問私有屬性,模塊也遵循這種約定, 如果模塊名以單下劃線開頭,那from module import *時(shí)不能被導(dǎo)入, 但采用from module import _private_module依然可以導(dǎo)入。 調(diào)用一個(gè)模塊的功能時(shí)會(huì)遇到單下劃線開頭的(socket._socket,sys._home,sys._clear_type_cache), 這都是私有,原則上是供內(nèi)部調(diào)用的,作為外部調(diào)用時(shí)也可以用。python要像與其他編程語(yǔ)言一樣,嚴(yán)格控制屬性的訪問權(quán)限, 需借助內(nèi)置方法如__getattr__封裝與擴(kuò)展性
封裝在于明確區(qū)分內(nèi)外,使得類實(shí)現(xiàn)者可以修改封裝內(nèi)的東西而不影響外部調(diào)用者的代碼;而外部使用用者只知道一個(gè)接口(函數(shù)),只要接口(函數(shù))名、參數(shù)不變,使用者的代碼永遠(yuǎn)無(wú)需改變。
這就提供一個(gè)良好的合作基礎(chǔ)——或者說(shuō),只要接口這個(gè)基礎(chǔ)約定不變,則代碼改變不足為慮。
總結(jié)
以上是生活随笔為你收集整理的(第七集——第一章)python面向对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电大计算机应用基础形考任务4答案,最新国
- 下一篇: 【ZedBoard实验随笔】OV7670