日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

(第七集——第一章)python面向对象

發(fā)布時(shí)間:2023/12/29 python 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (第七集——第一章)python面向对象 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

程序設(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)一.

程序中,保證先定義類,后產(chǎn)生對(duì)象: 1. 在程序中特征用變量標(biāo)識(shí),技能用函數(shù)標(biāo)識(shí) 2. 因而類中最常見的無(wú)非是:變量和函數(shù)的定義 # 類的范例class OldboyStudent:def __init__(self, name, age, sex): # 定義構(gòu)造方法,無(wú)返回值self.name = nameself.age = ageself.sex = sexschool = 'oldboy'def learn(self):print('is learning')def eat(self):print('is eating')def sleep(self):print('is sleeping')##################################################### # 使用OldboyStudent類: s1 = OldboyStudent('tom', 1, '男') # 實(shí)例化類得到s1對(duì)象,同時(shí)執(zhí)行類中的構(gòu)造方法__init__() print(s1.__dict__) # {'name': 'tom', 'age': 1, 'sex': '男'} 查看用來(lái)存放類所有變量的字典 print(s1.__dir__()) # 查看用來(lái)存放類的變量和函數(shù)的名稱空間 print(s1.__module__) # __main__ 在當(dāng)前類執(zhí)行時(shí)的模塊名稱,當(dāng)被其它模塊引入時(shí),模塊名為引入該模塊的那個(gè)模塊名 print(s1.__class__) # <class '__main__.OldboyStudent'> 查看對(duì)象對(duì)應(yīng)的類名 print(s1.__getattribute__('school')) # 類的反射,通過字符串獲得對(duì)象中該屬性的屬性值 # del s1.age # 等價(jià)于s1.__dict__.pop('age'), 從名稱空間字典中刪除該屬性 # print(s1.__dict__) # s1.age = 22 # 為age屬性賦值 # print(s1.age) # 訪問age屬性 s1.learn() # 調(diào)用learn方法 s1.eat() # 調(diào)用eat方法

屬性查找


類的屬性

# 數(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ù)selfself可以是任意名字,但是約定俗成地寫出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接口

模仿interface: python中無(wú)interface的關(guān)鍵字,可借助第三方模塊模仿接口的概念,twisted的twisted\internet\interface.py里使用zope.interface 也可采用繼承實(shí)現(xiàn):繼承的兩種用途: 1、繼承基類的方法,并且做出自己的改變或者擴(kuò)展(代碼重用):實(shí)踐中,繼承的這種用途意義并不很大,甚至常常是有害的。因?yàn)樗沟米宇惻c基類出現(xiàn)強(qiáng)耦合。 2:聲明某個(gè)子類兼容于某基類,定義一個(gè)接口類(模仿java的Interface),接口類中定義了一些接口名(就是函數(shù)名)且并未實(shí)現(xiàn)接口的功能,子類繼承接口類,并且實(shí)現(xiàn)接口中的功能class Interface:#定義接口Interface類來(lái)模仿接口的概念,python中壓根就沒有interface關(guān)鍵字來(lái)定義一個(gè)接口。def read(self): #定接口函數(shù)readpassdef write(self): #定義接口函數(shù)writepassclass Txt(Interface): # 繼承Interface類作為父類,具體實(shí)現(xiàn)read和writedef read(self): # 重寫父類中的方法 print('文本數(shù)據(jù)的讀取方法')def write(self):print('文本數(shù)據(jù)的讀取方法')class Sata(Interface): #磁盤,具體實(shí)現(xiàn)read和writedef read(self):print('硬盤數(shù)據(jù)的讀取方法')def write(self):print('硬盤數(shù)據(jù)的讀取方法')class Process(Interface):def read(self):print('進(jìn)程數(shù)據(jù)的讀取方法')def write(self):print('進(jìn)程數(shù)據(jù)的讀取方法')

抽象類

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)抽象方法.

python中實(shí)現(xiàn)抽象類: #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import abc #利用abc模塊實(shí)現(xiàn)抽象類class All_file(metaclass=abc.ABCMeta): # 繼承abc類all_type='file' # 定義數(shù)據(jù)屬性@abc.abstractmethod #定義抽象方法,無(wú)需實(shí)現(xiàn)功能def read(self):'子類必須定義讀功能'pass@abc.abstractmethod #定義抽象方法,無(wú)需實(shí)現(xiàn)功能def write(self):'子類必須定義寫功能'pass# class Txt(All_file): # 繼承All_file作為父類 # pass # # t1=Txt() #報(bào)錯(cuò),子類沒有定義抽象方法class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('文本數(shù)據(jù)的讀取方法')def write(self):print('文本數(shù)據(jù)的讀取方法')class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('硬盤數(shù)據(jù)的讀取方法')def write(self):print('硬盤數(shù)據(jù)的讀取方法')class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('進(jìn)程數(shù)據(jù)的讀取方法')def write(self):print('進(jìn)程數(shù)據(jù)的讀取方法')wenbenwenjian=Txt()yingpanwenjian=Sata()jinchengwenjian=Process()#這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read()print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)

抽象類與接口:
抽象類的本質(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)先

當(dāng)類是經(jīng)典類時(shí),按深度優(yōu)先方式查找下去。 當(dāng)類是新式類時(shí),按廣度優(yōu)先方式查找下去。class A(object):def test(self):print('from A')class B(A):def test(self):print('from B')class C(A):def test(self):print('from C')class D(B):def test(self):print('from D')class E(C):def test(self):print('from E')class F(D,E):# def test(self):# print('from F')pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個(gè)屬性可以查看線性列表,經(jīng)典類沒有這個(gè)屬性#新式類繼承順序:F->D->B->E->C->A #經(jīng)典類繼承順序:F->D->B->A->E->C #python3中統(tǒng)一都是新式類 #pyhon2中才分新式類與經(jīng)典類

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)性:如下

peo=People() dog=Dog() pig=Pig()#peo、dog、pig都是動(dòng)物,只要是動(dòng)物肯定有talk方法 #于是我們可以不用考慮它們?nèi)叩木唧w是什么類型,而直接使用 peo.talk() dog.talk() pig.talk()#更進(jìn)一步,我們可以定義一個(gè)統(tǒng)一的接口來(lái)使用 def func(obj): # 傳入不同的obj對(duì)象,調(diào)用不同對(duì)象中的talk方法obj.talk() 多態(tài)性的好處: 1.增加了程序的靈活性:以不變應(yīng)萬(wàn)變,不論對(duì)象千變?nèi)f化,使用者都是同一種形式去調(diào)用,如func(animal) 2.增加了程序的可擴(kuò)展性通過繼承animal類創(chuàng)建了一個(gè)新的類,使用者無(wú)需更改自己的代碼,還是用func(animal)去調(diào)用  >>> class Cat(Animal): #屬于動(dòng)物的另外一種形態(tài):貓 ... def talk(self): ... print('say miao') ... >>> def func(animal): #對(duì)于使用者來(lái)說(shuō),自己的代碼根本無(wú)需改動(dòng) ... animal.talk() ... >>> cat1=Cat() #實(shí)例出一只貓 >>> func(cat1) #甚至連調(diào)用方式也無(wú)需改變,就能調(diào)用貓的talk功能 say miao''' 這樣我們新增了一個(gè)形態(tài)Cat,由Cat類產(chǎn)生的實(shí)例cat1,使用者可以在完全不需要修改自己代碼的情況下。使用和人、狗、豬一樣的方式調(diào)用cat1的talk方法,即func(cat1) ''' Python崇尚鴨子類型,即‘如果看起來(lái)像、叫聲像而且走起路來(lái)像鴨子,那么它就是鴨子’python程序員通常根據(jù)這種行為來(lái)編寫程序。 例如,如果想編寫現(xiàn)有對(duì)象的自定義版本,可以繼承該對(duì)象也可以創(chuàng)建一個(gè)外觀和行為像,但與它無(wú)任何關(guān)系的全新對(duì)象,后者通常用于保存程序組件的松耦合度。例1:利用標(biāo)準(zhǔn)庫(kù)中定義的各種‘與文件類似’的對(duì)象,盡管這些對(duì)象的工作方式像文件,但他們沒有繼承內(nèi)置文件對(duì)象的方法 #二者都像鴨子,二者看起來(lái)都像文件,因而就可以當(dāng)文件一樣去用 class TxtFile:def read(self):passdef write(self):passclass DiskFile:def read(self):passdef write(self):pass2:其實(shí)大家一直在享受著多態(tài)性帶來(lái)的好處,比如Python的序列類型有多種形態(tài):字符串,列表,元組,多態(tài)性體現(xiàn)如下 #str,list,tuple都是序列類型 s=str('hello') l=list([1,2,3]) t=tuple((4,5,6))#我們可以在不考慮三者類型的前提下使用s,l,t s.__len__() l.__len__() t.__len__()len(s) len(l) len(t)

封裝


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ǔ)約定不變,則代碼改變不足為慮。

封裝在于明確區(qū)分內(nèi)外,使得類實(shí)現(xiàn)者可以修改封裝內(nèi)的東西而不影響外部調(diào)用者的代碼; 而外部使用者只知道一個(gè)接口(函數(shù)),只要接口(函數(shù))名、參數(shù)不變,使用者的代碼永遠(yuǎn)無(wú)需改變。 這就提供一個(gè)良好的合作基礎(chǔ)——或者說(shuō),只要接口這個(gè)基礎(chǔ)約定不變,則代碼改變不足為慮。#類的設(shè)計(jì)者 class Room:def __init__(self,name,owner,width,length,high):self.name=nameself.owner=ownerself.__width=width # 封裝屬性self.__length=lengthself.__high=highdef tell_area(self): # 對(duì)外提供的接口,隱藏了內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),此時(shí)我們想求的是面積return self.__width * self.__length#使用者 >>> r1=Room('臥室','egon',20,20,20) >>> r1.tell_area() #使用者調(diào)用接口tell_area#類的設(shè)計(jì)者,輕松的擴(kuò)展了功能,而類的使用者完全不需要改變自己的代碼 class Room:def __init__(self,name,owner,width,length,high):self.name=nameself.owner=ownerself.__width=widthself.__length=lengthself.__high=highdef tell_area(self): #對(duì)外提供的接口,隱藏內(nèi)部實(shí)現(xiàn),此時(shí)我們想求的是體積,內(nèi)部邏輯變了,只需求修該下列一行就可以很簡(jiǎn)答的實(shí)現(xiàn),而且外部調(diào)用感知不到,仍然使用該方法,但是功能已經(jīng)變了return self.__width * self.__length * self.__high#對(duì)于仍然在使用tell_area接口的人來(lái)說(shuō),根本無(wú)需改動(dòng)自己的代碼,就可以用上新功能 >>> r1.tell_area()

總結(jié)

以上是生活随笔為你收集整理的(第七集——第一章)python面向对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。