23 Python 面向对象
面向過(guò)程 VS 面向?qū)ο?
面向過(guò)程的程序設(shè)計(jì)的核心是過(guò)程(流水線式思維),過(guò)程即解決問(wèn)題的步驟,面向過(guò)程的設(shè)計(jì)就好比精心設(shè)計(jì)好一條流水線,考慮周全什么時(shí)候處理什么東西。
優(yōu)點(diǎn)是:極大的降低了寫(xiě)程序的復(fù)雜度,只需要順著要執(zhí)行的步驟,堆疊代碼即可。
缺點(diǎn)是:一套流水線或者流程就是用來(lái)解決一個(gè)問(wèn)題,代碼牽一發(fā)而動(dòng)全身。
應(yīng)用場(chǎng)景:一旦完成基本很少改變的場(chǎng)景,著名的例子有Linux內(nèi)核,git,以及Apache HTTP Server等。
?
面向?qū)ο蟮某绦蛟O(shè)計(jì)的核心是對(duì)象(上帝式思維),要理解對(duì)象為何物,必須把自己當(dāng)成上帝,上帝眼里世間存在的萬(wàn)物皆為對(duì)象,不存在的也可以創(chuàng)造出來(lái)。面向?qū)ο蟮某绦蛟O(shè)計(jì)好比如來(lái)設(shè)計(jì)西游記,如來(lái)要解決的問(wèn)題是把經(jīng)書(shū)傳給東土大唐,如來(lái)想了想解決這個(gè)問(wèn)題需要四個(gè)人:唐僧,沙和尚,豬八戒,孫悟空,每個(gè)人都有各自的特征和技能(這就是對(duì)象的概念,特征和技能分別對(duì)應(yīng)對(duì)象的屬性和方法),然而這并不好玩,于是如來(lái)又安排了一群妖魔鬼怪,為了防止師徒四人在取經(jīng)路上被搞死,又安排了一群神仙保駕護(hù)航,這些都是對(duì)象。然后取經(jīng)開(kāi)始,師徒四人與妖魔鬼怪神仙互相纏斗著直到最后取得真經(jīng)。如來(lái)根本不會(huì)管師徒四人按照什么流程去取。
面向?qū)ο蟮某绦蛟O(shè)計(jì)的
優(yōu)點(diǎn)是:解決了程序的擴(kuò)展性。對(duì)某一個(gè)對(duì)象單獨(dú)修改,會(huì)立刻反映到整個(gè)體系中,如對(duì)游戲中一個(gè)人物參數(shù)的特征和技能修改都很容易。
缺點(diǎn):可控性差,無(wú)法向面向過(guò)程的程序設(shè)計(jì)流水線式的可以很精準(zhǔn)的預(yù)測(cè)問(wèn)題的處理流程與結(jié)果,面向?qū)ο蟮某绦蛞坏╅_(kāi)始就由對(duì)象之間的交互解決問(wèn)題,即便是上帝也無(wú)法預(yù)測(cè)最終結(jié)果。于是我們經(jīng)常看到一個(gè)游戲人某一參數(shù)的修改極有可能導(dǎo)致陰霸的技能出現(xiàn),一刀砍死3個(gè)人,這個(gè)游戲就失去平衡。
應(yīng)用場(chǎng)景:需求經(jīng)常變化的軟件,一般需求的變化都集中在用戶層,互聯(lián)網(wǎng)應(yīng)用,企業(yè)內(nèi)部軟件,游戲等都是面向?qū)ο蟮某绦蛟O(shè)計(jì)大顯身手的好地方。
在python 中面向?qū)ο蟮某绦蛟O(shè)計(jì)并不是全部。
面向?qū)ο缶幊炭梢允钩绦虻木S護(hù)和擴(kuò)展變得更簡(jiǎn)單,并且可以大大提高程序開(kāi)發(fā)效率 ,另外,基于面向?qū)ο蟮某绦蚩梢允顾烁尤菀桌斫饽愕拇a邏輯,從而使團(tuán)隊(duì)開(kāi)發(fā)變得更從容。
了解一些名詞:類、對(duì)象、實(shí)例、實(shí)例化
類:具有相同特征的一類事物(人、狗、老虎)
對(duì)象/實(shí)例:具體的某一個(gè)事物(隔壁阿花、樓下旺財(cái))
初識(shí)類和對(duì)象
python中一切皆為對(duì)象,類型的本質(zhì)就是類,所以,不管你信不信,你已經(jīng)使用了很長(zhǎng)時(shí)間的類了
在python中,用變量表示特征,用函數(shù)表示技能,因而具有相同特征和技能的一類事物就是‘類’,對(duì)象是則是這一類事物中具體的一個(gè)。
類的相關(guān)知識(shí)
聲明類
1 class Person: #定義一個(gè)人類 2 role = 'person' #人的角色屬性都是人 3 def walk(self): #人都可以走路,也就是有一個(gè)走路方法,也叫動(dòng)態(tài)屬性 4 print("person is walking...") 類的聲明類有兩種作用:屬性引用和實(shí)例化
?屬性引用(類名.屬性)
class Person: #定義一個(gè)人類role = 'person' #人的角色屬性都是人 def walk(self): #人都可以走路,也就是有一個(gè)走路方法 print("person is walking...") print(Person.role) #查看人的role屬性 print(Person.walk) #引用人的走路方法,注意,這里不是在調(diào)用實(shí)例化:類名加括號(hào)就是實(shí)例化,會(huì)自動(dòng)觸發(fā)__init__函數(shù)的運(yùn)行,可以用它來(lái)為每個(gè)實(shí)例定制自己的特征
class Person: #定義一個(gè)人類role = 'person' #人的角色屬性都是人 def __init__(self,name): self.name = name # 每一個(gè)角色都有自己的昵稱; def walk(self): #人都可以走路,也就是有一個(gè)走路方法 print("person is walking...") print(Person.role) #查看人的role屬性 print(Person.walk) #引用人的走路方法,注意,這里不是在調(diào)用實(shí)例化的過(guò)程就是類——>對(duì)象的過(guò)程
原本我們只有一個(gè)Person類,在這個(gè)過(guò)程中,產(chǎn)生了一個(gè)egg對(duì)象,有自己具體的名字、攻擊力和生命值。
語(yǔ)法:對(duì)象名 = 類名(參數(shù))
egg = Person('egon') #類名()就等于在執(zhí)行Person.__init__() #執(zhí)行完__init__()就會(huì)返回一個(gè)對(duì)象。這個(gè)對(duì)象類似一個(gè)字典,存著屬于這個(gè)人本身的一些屬性和方法。 #你可以偷偷的理解:egg = {'name':'egon','walk':walk}查看屬性&調(diào)用方法
print(egg.name) #查看屬性直接 對(duì)象名.屬性名 print(egg.walk()) #調(diào)用方法,對(duì)象名.方法名()關(guān)于self
self:在實(shí)例化時(shí)自動(dòng)將對(duì)象/實(shí)例本身傳給__init__的第一個(gè)參數(shù),你也可以給他起個(gè)別的名字,但是正常人都不會(huì)這么做。
因?yàn)槟阆垢膭e人就不認(rèn)識(shí)
類屬性的補(bǔ)充
一:我們定義的類的屬性到底存到哪里了?有兩種方式查看 dir(類名):查出的是一個(gè)名字列表 類名.__dict__:查出的是一個(gè)字典,key為屬性名,value為屬性值二:特殊的類屬性 類名.__name__# 類的名字(字符串) 類名.__doc__# 類的文檔字符串 類名.__base__# 類的第一個(gè)父類(在講繼承時(shí)會(huì)講) 類名.__bases__# 類所有父類構(gòu)成的元組(在講繼承時(shí)會(huì)講) 類名.__dict__# 類的字典屬性 類名.__module__# 類定義所在的模塊 類名.__class__# 實(shí)例對(duì)應(yīng)的類(僅新式類中)對(duì)象的相關(guān)知識(shí)
對(duì)象是關(guān)于類而實(shí)際存在的一個(gè)例子,即實(shí)例
對(duì)象/實(shí)例只有一種作用:屬性引用
當(dāng)然了,你也可以引用一個(gè)方法,因?yàn)榉椒ㄒ彩且粋€(gè)屬性,只不過(guò)是一個(gè)類似函數(shù)的屬性,我們也管它叫動(dòng)態(tài)屬性。引用動(dòng)態(tài)屬性并不是執(zhí)行這個(gè)方法,要想調(diào)用方法和調(diào)用函數(shù)是一樣的,都需要在后面加上括號(hào) 1 def Person(*args,**kwargs): 2 self = {} 3 def attack(self,dog): 4 dog['life_value'] -= self['aggressivity'] 5 6 def __init__(name,aggressivity,life_value): 7 self['name'] = name 8 self['aggressivity'] = aggressivity 9 self['life_value'] = life_value 10 self['attack'] = attack 11 12 __init__(*args,**kwargs) 13 return self 14 15 egg = Person('egon',78,10) 16 print(egg['name']) 了解對(duì)象 1 class 類名: 2 def __init__(self,參數(shù)1,參數(shù)2): 3 self.對(duì)象的屬性1 = 參數(shù)1 4 self.對(duì)象的屬性2 = 參數(shù)2 5 6 def 方法名(self):pass 7 8 def 方法名2(self):pass 9 10 對(duì)象名 = 類名(1,2) #對(duì)象就是實(shí)例,代表一個(gè)具體的東西 11 #類名() : 類名+括號(hào)就是實(shí)例化一個(gè)類,相當(dāng)于調(diào)用了__init__方法 12 #括號(hào)里傳參數(shù),參數(shù)不需要傳self,其他與init中的形參一一對(duì)應(yīng) 13 #結(jié)果返回一個(gè)對(duì)象 14 對(duì)象名.對(duì)象的屬性1 #查看對(duì)象的屬性,直接用 對(duì)象名.屬性名 即可 15 對(duì)象名.方法名() #調(diào)用類中的方法,直接用 對(duì)象名.方法名() 即可 對(duì)象小結(jié) 1 class Person: # 定義一個(gè)人類 2 role = 'person' # 人的角色屬性都是人 3 4 def __init__(self, name, aggressivity, life_value): 5 self.name = name # 每一個(gè)角色都有自己的昵稱; 6 self.aggressivity = aggressivity # 每一個(gè)角色都有自己的攻擊力; 7 self.life_value = life_value # 每一個(gè)角色都有自己的生命值; 8 9 def attack(self,dog): 10 # 人可以攻擊狗,這里的狗也是一個(gè)對(duì)象。 11 # 人攻擊狗,那么狗的生命值就會(huì)根據(jù)人的攻擊力而下降 12 dog.life_value -= self.aggressivity 13 14 class Dog: # 定義一個(gè)狗類 15 role = 'dog' # 狗的角色屬性都是狗 16 17 def __init__(self, name, breed, aggressivity, life_value): 18 self.name = name # 每一只狗都有自己的昵稱; 19 self.breed = breed # 每一只狗都有自己的品種; 20 self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; 21 self.life_value = life_value # 每一只狗都有自己的生命值; 22 23 def bite(self,people): 24 # 狗可以咬人,這里的狗也是一個(gè)對(duì)象。 25 # 狗咬人,那么人的生命值就會(huì)根據(jù)狗的攻擊力而下降 26 people.life_value -= self.aggressivity 27 28 egg = Person('egon',10,1000) #創(chuàng)造了一個(gè)實(shí)實(shí)在在的人egg 29 ha2 = Dog('二愣子','哈士奇',10,1000) #創(chuàng)造了一只實(shí)實(shí)在在的狗ha2 30 print(ha2.life_value) #看看ha2的生命值 31 egg.attack(ha2) #egg打了ha2一下 32 print(ha2.life_value) #ha2掉了10點(diǎn)血 游戲全解
1 from math import pi 2 3 class Circle: 4 ''' 5 定義了一個(gè)圓形類; 6 提供計(jì)算面積(area)和周長(zhǎng)(perimeter)的方法 7 ''' 8 def __init__(self,radius): 9 self.radius = radius 10 11 def area(self): 12 return pi * self.radius * self.radius 13 14 def perimeter(self): 15 return 2 * pi *self.radius 16 17 18 circle = Circle(10) #實(shí)例化一個(gè)圓 19 area1 = circle.area() #計(jì)算圓面積 20 per1 = circle.perimeter() #計(jì)算圓周長(zhǎng) 21 print(area1,per1) #打印圓面積和周長(zhǎng) 計(jì)算圓的周長(zhǎng)和面積
對(duì)象之間的交互
創(chuàng)建一個(gè)人類
1 class Person: # 定義一個(gè)人類 2 role = 'Person' # 人的角色屬性都是人 3 4 def __init__(self, name, sex, aggressivity, life_value): 5 self.name = name # 每一個(gè)人都有自己的昵稱; 6 self.sex = sex # 每一個(gè)人都有自己的性別; 7 self.aggressivity = aggressivity # 每一個(gè)人都有自己的攻擊力; 8 self.life_value = life_value # 每一個(gè)人都有自己的生命值; 9 10 def attack(self,people): 11 # 人攻擊狗,這里的人也是一個(gè)對(duì)象。 12 # 人攻擊狗,那么狗的生命值就會(huì)根據(jù)人的攻擊力而下降 13 people.life_value -= self.aggressivit Person創(chuàng)建一個(gè)狗類
1 class Dog: # 定義一個(gè)狗類 2 role = 'dog' # 狗的角色屬性都是狗 3 4 def __init__(self, name, breed, aggressivity, life_value): 5 self.name = name # 每一只狗都有自己的昵稱; 6 self.breed = breed # 每一只狗都有自己的品種; 7 self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; 8 self.life_value = life_value # 每一只狗都有自己的生命值; 9 10 def bite(self,people): 11 # 狗可以咬人,這里的狗也是一個(gè)對(duì)象。 12 # 狗咬人,那么人的生命值就會(huì)根據(jù)狗的攻擊力而下降 13 dog.life_value -= self.aggressivit Dog實(shí)例化一只實(shí)實(shí)在在的二哈
ha2 = Dog('二愣子','哈士奇',10,1000) #創(chuàng)造了一只實(shí)實(shí)在在的狗ha2?
交互 egon打ha2一下
print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10點(diǎn)血完整的代碼
1 class Person: # 定義一個(gè)人類 2 role = 'person' # 人的角色屬性都是人 3 4 def __init__(self, name, aggressivity, life_value): 5 self.name = name # 每一個(gè)角色都有自己的昵稱; 6 self.aggressivity = aggressivity # 每一個(gè)角色都有自己的攻擊力; 7 self.life_value = life_value # 每一個(gè)角色都有自己的生命值; 8 9 def attack(self,dog): 10 # 人可以攻擊狗,這里的狗也是一個(gè)對(duì)象。 11 # 人攻擊狗,那么狗的生命值就會(huì)根據(jù)人的攻擊力而下降 12 dog.life_value -= self.aggressivity 13 14 class Dog: # 定義一個(gè)狗類 15 role = 'dog' # 狗的角色屬性都是狗 16 17 def __init__(self, name, breed, aggressivity, life_value): 18 self.name = name # 每一只狗都有自己的昵稱; 19 self.breed = breed # 每一只狗都有自己的品種; 20 self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; 21 self.life_value = life_value # 每一只狗都有自己的生命值; 22 23 def bite(self,people): 24 # 狗可以咬人,這里的狗也是一個(gè)對(duì)象。 25 # 狗咬人,那么人的生命值就會(huì)根據(jù)狗的攻擊力而下降 26 people.life_value -= self.aggressivity 27 28 egg = Person('egon',10,1000) #創(chuàng)造了一個(gè)實(shí)實(shí)在在的人egg 29 ha2 = Dog('二愣子','哈士奇',10,1000) #創(chuàng)造了一只實(shí)實(shí)在在的狗ha2 30 print(ha2.life_value) #看看ha2的生命值 31 egg.attack(ha2) #egg打了ha2一下 32 print(ha2.life_value) #ha2掉了10點(diǎn)血 egon大戰(zhàn)哈士奇 1 from math import pi 2 3 class Circle: 4 ''' 5 定義了一個(gè)圓形類; 6 提供計(jì)算面積(area)和周長(zhǎng)(perimeter)的方法 7 ''' 8 def __init__(self,radius): 9 self.radius = radius 10 11 def area(self): 12 return pi * self.radius * self.radius 13 14 def perimeter(self): 15 return 2 * pi *self.radius 16 17 18 circle = Circle(10) #實(shí)例化一個(gè)圓 19 area1 = circle.area() #計(jì)算圓面積 20 per1 = circle.perimeter() #計(jì)算圓周長(zhǎng) 21 print(area1,per1) #打印圓面積和周長(zhǎng) 簡(jiǎn)單的類類命名空間與對(duì)象、實(shí)例的命名空間
創(chuàng)建一個(gè)類就會(huì)創(chuàng)建一個(gè)類的名稱空間,用來(lái)存儲(chǔ)類中定義的所有名字,這些名字稱為類的屬性
而類有兩種屬性:靜態(tài)屬性和動(dòng)態(tài)屬性
- 靜態(tài)屬性就是直接在類中定義的變量
- 動(dòng)態(tài)屬性就是定義在類中的方法
其中類的數(shù)據(jù)屬性是共享給所有對(duì)象的
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072?
而類的動(dòng)態(tài)屬性是綁定到所有對(duì)象的
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>?創(chuàng)建一個(gè)對(duì)象/實(shí)例就會(huì)創(chuàng)建一個(gè)對(duì)象/實(shí)例的名稱空間,存放對(duì)象/實(shí)例的名字,稱為對(duì)象/實(shí)例的屬性
在obj.name會(huì)先從obj自己的名稱空間里找name,找不到則去類中找,類也找不到就找父類...最后都找不到就拋出異常
面向?qū)ο蟮慕M合用法
軟件重用的重要方式除了繼承之外還有另外一種方式,即:組合
組合指的是,在一個(gè)類中以另外一個(gè)類的對(duì)象作為數(shù)據(jù)屬性,稱為類的組合
1 class Person: # 定義一個(gè)人類 2 role = 'person' # 人的角色屬性都是人 3 4 def __init__(self, name, aggr): 5 self.name = name # 每一個(gè)角色都有自己的昵稱; 6 self.aggr = aggr 7 self.weanpon = Weapon() # 給人增加一個(gè)技能 8 9 10 egg = Person('egon', 5) 11 egg.weanpon.prick(egg) 12 print(egg.aggr) 對(duì)象組合圓環(huán)是由兩個(gè)圓組成的,圓環(huán)的面積是外面圓的面積減去內(nèi)部圓的面積。圓環(huán)的周長(zhǎng)是內(nèi)部圓的周長(zhǎng)加上外部圓的周長(zhǎng)。
這個(gè)時(shí)候,我們就首先實(shí)現(xiàn)一個(gè)圓形類,計(jì)算一個(gè)圓的周長(zhǎng)和面積。然后在"環(huán)形類"中組合圓形的實(shí)例作為自己的屬性來(lái)用
用組合的方式建立了類與組合的類之間的關(guān)系,它是一種‘有’的關(guān)系,比如教授有生日,教授教python課程
1 class BirthData: 2 def __init__(self, year, month, day): 3 self.year = year 4 self.month = month 5 self.day = day 6 7 class Couse: 8 def __init__(self, name, price, period): 9 self.name = name 10 self.price = price 11 self.period = period 12 13 14 class Teacher: 15 def __init__(self, name, gender, birth, course): 16 self.name = name 17 self.gender = gender 18 self.birth = birth 19 self.course = course 20 21 def teach(self): 22 print('teaching') 23 24 25 p1 = Teacher('egon', 'male', BirthData('l995', '1', '27'), Couse('python', '2800', '4 month')) 26 print(p1.birth.year, p1.birth.month, p1.birth.day) 27 print(p1.course.name, p1.course.price, p1.course.period) View Code當(dāng)類之間有顯著不同,并且較小的類是較大的類所需要的組件時(shí),用組合比較好
初識(shí)面向?qū)ο笮〗Y(jié)
1 # 定義一個(gè)人類 2 class Person: 3 role = 'person' 4 5 def __init__(self, name, aggressivity, life_value, money): 6 self.name = name 7 self.aggressivity = aggressivity 8 self.life_value = life_value 9 self.money = money 10 11 def attack(self, obj): 12 obj.life_value -= self.aggressivity 13 14 15 # 定義一個(gè)狗類 16 class Dog: 17 role = 'dog' 18 19 def __init__(self, name, breed, aggressivity, life_value): 20 self.name = name 21 self.breed = breed 22 self.aggressivity = aggressivity 23 self.life_value = life_value 24 25 def bite(self, obj): 26 obj.life_value -= self.aggressivity 27 28 29 # 定義一個(gè)武器和技能類 30 class Weapon: 31 def __init__(self, name, price, agrev, life_value): 32 self.name = name 33 self.price = price 34 self.agrev = agrev 35 self.life_value = life_value 36 37 def update(self, obj): # obj就是要帶這個(gè)裝備的人 38 if obj.money > self.price: 39 obj.money -= self.price # 用這個(gè)武器的人花錢(qián)買所以對(duì)應(yīng)的錢(qián)要減少 40 obj.aggressivity += self.agrev # 帶上這個(gè)裝備可以讓人增加攻擊 41 obj.life_value += self.life_value # 帶上這個(gè)裝備可以讓人增加生命值 42 43 def prick(self, obj): # 這是該裝備的主動(dòng)技能,扎死對(duì)方 44 obj.life_value -= 500 # 攻擊力是500 45 46 47 # 測(cè)試交互 48 lance = Weapon('長(zhǎng)矛', 200, 6, 100) 49 egg = Person('egon', 10, 1000, 600) # 創(chuàng)造了一個(gè)實(shí)實(shí)在在的人egg 50 ha2 = Dog('二哈', '哈士奇', 10, 1000) # 創(chuàng)造了一只實(shí)實(shí)在在的狗ha2 51 lance.update(egg) # egg花錢(qián)買了一個(gè)長(zhǎng)矛防身,且自身屬性得到了提高 52 egg.weapon = lance # egg裝備上了武器 53 print(egg.__dict__) 54 55 print(ha2.life_value) 56 egg.attack(ha2) # egg打了ha2一下 57 print(ha2.life_value) 58 egg.weapon.prick(ha2) # 發(fā)動(dòng)武器技能 59 print(ha2.life_value) 人狗大戰(zhàn)交互?
?
面向?qū)ο蟮娜筇匦?/h1> 繼承
什么是繼承
繼承是一種創(chuàng)建新類的方式,在python中,新建的類可以繼承一個(gè)或多個(gè)父類,父類又可稱為基類或超類,新建的類稱為派生類或子類
python中類的繼承分為:單繼承和多繼承
class ParentClass1: #定義父類passclass ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號(hào)分隔開(kāi)多個(gè)繼承的類 pass查看繼承
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個(gè)子類,__bases__則是查看所有繼承的父類 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)提示:如果沒(méi)有指定基類,python的類會(huì)默認(rèn)繼承object類,object是所有python類的基類,它提供了一些常見(jiàn)方法(如__str__)的實(shí)現(xiàn)。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)繼承與抽象(先抽象再繼承)
抽象即抽取類似或者說(shuō)比較像的部分。
抽象分成兩個(gè)層次:?
1.將奧巴馬和梅西這倆對(duì)象比較像的部分抽取成類;?
2.將人,豬,狗這三個(gè)類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關(guān)注點(diǎn),降低復(fù)雜度)
?
繼承:是基于抽象的結(jié)果,通過(guò)編程語(yǔ)言去實(shí)現(xiàn)它,肯定是先經(jīng)歷抽象這個(gè)過(guò)程,才能通過(guò)繼承的方式去表達(dá)出抽象的結(jié)構(gòu)。
抽象只是分析和設(shè)計(jì)的過(guò)程中,一個(gè)動(dòng)作或者說(shuō)一種技巧,通過(guò)抽象可以得到類
派生
當(dāng)然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會(huì)影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調(diào)用新增的屬性時(shí),就以自己為準(zhǔn)了
1 class Animal: 2 """ 3 人和狗都是動(dòng)物,所以創(chuàng)造一個(gè)Animal基類 4 """ 5 def __init__(self, name, aggressivity, life_value): 6 self.name = name 7 self.aggressivity = aggressivity 8 self.life_value = life_value 9 10 def eat(self): 11 print('%s is eating' % self.name) 12 13 14 class Dog(Animal): 15 """ 16 狗類,繼承了Animal類 17 """ 18 def bite(self, people): 19 """ 20 派生,狗咬人的技能 21 :param people: 22 :return: 23 """ 24 people.life_value -= self.aggressivity 25 26 class Person(Animal): 27 """ 28 人類,繼承Animal 29 """ 30 def attack(self, dog): 31 """ 32 派生,人有攻擊的技能 33 :param dog: 34 :return: 35 """ 36 dog.life_value -= self.aggressivity 37 38 39 egg = Person('egon', 10, 1000) 40 ha2 = Dog('二愣子', 50, 1000) 41 42 print(ha2.life_value) 43 print(egg.attack(ha2)) 44 print(ha2.life_value) View Code注意:像ha2.life_value之類的屬性引用,會(huì)先從實(shí)例中找life_value然后去類中找,然后再去父類中找...直到最頂級(jí)的父類。
?
在子類中,新建的重名的函數(shù)屬性,在編輯函數(shù)內(nèi)功能的時(shí)候,有可能需要重用父類中重名的那個(gè)函數(shù)功能,應(yīng)該是用調(diào)用普通函數(shù)的方式,即:類名.func(),此時(shí)就與調(diào)用普通函數(shù)無(wú)異了,因此即便是self參數(shù)也要為其傳值.
在python3中,子類執(zhí)行父類的方法也可以直接用super方法.
1 class Animal: 2 """ 3 人和狗都是動(dòng)物,所以創(chuàng)造一個(gè)Animal基類 4 """ 5 def __init__(self, name, aggressivity, life_value): 6 self.name = name 7 self.aggressivity = aggressivity 8 self.life_value = life_value 9 10 def eat(self): 11 print('%s is eating' % self.life_value) 12 13 14 class Dog(Animal): 15 """ 16 狗類, 繼承Animal類 17 """ 18 def __init__(self, name, aggressivity, life_value, breed): 19 super().__init__(name, aggressivity, life_value) 20 self.breed = breed 21 22 def breed(self, people): 23 """ 24 派生出新的技能, 狗咬人的技能 25 :param people: 26 :return: 27 """ 28 people.life_value -= self.aggressivity 29 30 def eat(self): 31 # super().eat() 32 # Animal.eat(self) 33 print('from dog') 34 35 36 class Person(Animal): 37 """ 38 人類,繼承Animal類 39 """ 40 def __init__(self, name, aggressivity, life_value, money): 41 Animal.__init__(self, name, aggressivity, life_value) 42 # super.__init__(name, aggressivity, life_value) 43 self.money = money 44 45 def attack(self, dog): 46 """ 47 派生出了新技能, 人有攻擊的技能 48 :param dog: 49 :return: 50 """ 51 dog.life_value -= self.aggressivity 52 53 54 egg = Person('egon', 10, 1000, 600) 55 ha2 = Dog('二愣子', '哈士奇', 10, 1000) 56 print(egg.name) 57 print(ha2.name) 58 egg.eat() 59 ha2.eat() View Code通過(guò)繼承建立了派生類與基類之間的關(guān)系,它是一種'是'的關(guān)系,比如白馬是馬,人是動(dòng)物。
當(dāng)類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如教授是老師
1 class Teacher: 2 def __init__(self, name, gender): 3 self.name = name 4 self.gender = gender 5 6 def teach(self): 7 print('teaching') 8 9 class Professor(Teacher): 10 pass 11 12 p1 = Professor('egon', 'male') 13 p1.teach() View Code鉆石繼承
繼承順序
1 class A(object): 2 def test(self): 3 print('from A') 4 5 class B(A): 6 def test(self): 7 print('from B') 8 9 class C(A): 10 def test(self): 11 print('from C') 12 13 class D(B): 14 def test(self): 15 print('from D') 16 17 class E(C): 18 def test(self): 19 print('from E') 20 21 class F(D,E): 22 # def test(self): 23 # print('from F') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有這個(gè)屬性可以查看線性列表,經(jīng)典類沒(méi)有這個(gè)屬性 28 29 #新式類繼承順序:F->D->B->E->C->A 30 #經(jīng)典類繼承順序:F->D->B->A->E->C 31 #python3中統(tǒng)一都是新式類 32 #pyhon2中才分新式類與經(jīng)典類 繼承順序繼承原理
python到底是如何實(shí)現(xiàn)繼承的,對(duì)于你定義的每一個(gè)類,python會(huì)計(jì)算出一個(gè)方法解析順序(MRO)列表,這個(gè)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)繼承,python會(huì)在MRO列表上從左到右開(kāi)始查找基類,直到找到第一個(gè)匹配這個(gè)屬性的類為止。
而這個(gè)MRO列表的構(gòu)造是通過(guò)一個(gè)C3線性化算法來(lái)實(shí)現(xiàn)的。我們不去深究這個(gè)算法的數(shù)學(xué)原理,它實(shí)際上就是合并所有父類的MRO列表并遵循如下三條準(zhǔn)則:
1.子類會(huì)先于父類被檢查
2.多個(gè)父類會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查
3.如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類
繼承小結(jié)
繼承的作用
減少代碼的重用 提高代碼可讀性 規(guī)范編程模式幾個(gè)名詞
抽象:抽象即抽取類似或者說(shuō)比較像的部分。是一個(gè)從具體到抽象的過(guò)程。 繼承:子類繼承了父類的方法和屬性 派生:子類在父類方法和屬性的基礎(chǔ)上產(chǎn)生了新的方法和屬性抽象類與接口類
1.多繼承問(wèn)題 在繼承抽象類的過(guò)程中,我們應(yīng)該盡量避免多繼承; 而在繼承接口的時(shí)候,我們反而鼓勵(lì)你來(lái)多繼承接口2.方法的實(shí)現(xiàn) 在抽象類中,我們可以對(duì)一些抽象方法做出基礎(chǔ)實(shí)現(xiàn); 而在接口類中,任何方法都只是一種規(guī)范,具體的功能需要子類實(shí)現(xiàn)鉆石繼承
新式類:廣度優(yōu)先 經(jīng)典類:深度優(yōu)先?
多態(tài)
多態(tài)
多態(tài)指的是一類事物有多種形態(tài)
動(dòng)物有多種形態(tài):人,狗,豬
1 class Animal(metaclass=abc.ABCMeta): # 同一類事物:動(dòng)物 2 @abc.abstractmethod 3 def talk(self): 4 pass 5 6 7 class People(Animal): # 動(dòng)物的形態(tài)之一:人 8 def talk(self): 9 print('say hello') 10 11 12 class Dog(Animal): # 動(dòng)物的形態(tài)之二:狗 13 def talk(self): 14 print('say wangwang') 15 16 17 class pig(Animal): # 動(dòng)物的形態(tài)之三:豬 18 def talk(self): 19 print('say aoao') 動(dòng)物文件有多種形態(tài):文本文件,可執(zhí)行文件
1 import abc 2 class File(metaclass=abc.ABCMeta): # 同一類事物:文件 3 @abc.abstractmethod 4 def cilck(self): 5 pass 6 7 8 class Text(File): # 文件的形態(tài)之一:文本文件 9 def click(self): 10 print('open file') 11 12 13 class ExeFile(File): # 文件的形態(tài)之二:可執(zhí)行文件 14 def click(self): 15 print('execute file') 事物多態(tài)性
一 什么是多態(tài)動(dòng)態(tài)綁定(在繼承的背景下使用時(shí),有時(shí)也稱為多態(tài)性)
多態(tài)性是指在不考慮實(shí)例類型的情況下使用實(shí)例
在面向?qū)ο蠓椒ㄖ幸话闶沁@樣表述多態(tài)性: 向不同的對(duì)象發(fā)送同一條消息(!!!obj.func():是調(diào)用了obj的方法func,又稱為向obj發(fā)送了一條消息func),不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為(即方法)。 也就是說(shuō),每個(gè)對(duì)象可以用自己的方式去響應(yīng)共同的消息。所謂消息,就是調(diào)用函數(shù),不同的行為就是指不同的實(shí)現(xiàn),即執(zhí)行不同的函數(shù)。比如:老師.下課鈴響了(),學(xué)生.下課鈴響了(),老師執(zhí)行的是下班操作,學(xué)生執(zhí)行的是放學(xué)操作,雖然二者消息一樣,但是執(zhí)行的效果不同?
多態(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.talk()?
鴨子類型
逗比時(shí)刻:
Python崇尚鴨子類型,即‘如果看起來(lái)像、叫聲像而且走起路來(lái)像鴨子,那么它就是鴨子’
python程序員通常根據(jù)這種行為來(lái)編寫(xiě)程序。例如,如果想編寫(xiě)現(xiàn)有對(duì)象的自定義版本,可以繼承該對(duì)象
也可以創(chuàng)建一個(gè)外觀和行為像,但與它無(wú)任何關(guān)系的全新對(duì)象,后者通常用于保存程序組件的松耦合度。
例1:利用標(biāo)準(zhǔn)庫(kù)中定義的各種‘與文件類似’的對(duì)象,盡管這些對(duì)象的工作方式像文件,但他們沒(méi)有繼承內(nèi)置文件對(duì)象的方法
例2:序列類型有多種形態(tài):字符串,列表,元組,但他們直接沒(méi)有直接的繼承關(guān)系
1 # 二者都像鴨子,二者看起來(lái)都像文件,因而就可以當(dāng)文件一樣去用 2 class TxtFile: 3 def read(self): 4 pass 5 6 def write(self): 7 pass 8 9 10 class DiskFile: 11 def read(self): 12 pass 13 14 def write(self): 15 pass 鴨子類型?
封裝
【封裝】
?? ????? 隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外提供公共訪問(wèn)方式。
【好處】?
1. 將變化隔離;?
2. 便于使用;
3. 提高復(fù)用性;?
4. 提高安全性;
【封裝原則】
????? 1. 將不需要對(duì)外提供的內(nèi)容都隱藏起來(lái);
????? 2. 把屬性都隱藏,提供公共方法對(duì)其訪問(wèn)。
私有變量和私有方法
在python中用雙下劃線開(kāi)頭的方式將屬性隱藏起來(lái)(設(shè)置成私有的)
私有變量
這種自動(dòng)變形的特點(diǎn):
1.類中定義的__x只能在內(nèi)部使用,如self.__x,引用的就是變形的結(jié)果。
2.這種變形其實(shí)正是針對(duì)外部的變形,在外部是無(wú)法通過(guò)__x這個(gè)名字訪問(wèn)到的。
3.在子類定義的__x不會(huì)覆蓋在父類定義的__x,因?yàn)樽宇愔凶冃纬闪?#xff1a;_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開(kāi)頭的屬性在繼承給子類時(shí),子類是無(wú)法覆蓋的。
?
這種變形需要注意的問(wèn)題是:
1.這種機(jī)制也并沒(méi)有真正意義上限制我們從外部直接訪問(wèn)屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問(wèn)了,如a._A__N
2.變形的過(guò)程只在類的內(nèi)部生效,在定義后的賦值操作,不會(huì)變形
私有方法
3.在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的
?
封裝與擴(kuò)展性
封裝在于明確區(qū)分內(nèi)外,使得類實(shí)現(xiàn)者可以修改封裝內(nèi)的東西而不影響外部調(diào)用者的代碼;而外部使用用者只知道一個(gè)接口(函數(shù)),只要接口(函數(shù))名、參數(shù)不變,使用者的代碼永遠(yuǎn)無(wú)需改變。這就提供一個(gè)良好的合作基礎(chǔ)——或者說(shuō),只要接口這個(gè)基礎(chǔ)約定不變,則代碼改變不足為慮。
1 #類的設(shè)計(jì)者 2 class Room: 3 def __init__(self,name,owner,width,length,high): 4 self.name=name 5 self.owner=owner 6 self.__width=width 7 self.__length=length 8 self.__high=high 9 def tell_area(self): #對(duì)外提供的接口,隱藏了內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),此時(shí)我們想求的是面積 10 return self.__width * self.__length 11 12 13 #使用者 14 >>> r1=Room('臥室','egon',20,20,20) 15 >>> r1.tell_area() #使用者調(diào)用接口tell_area 16 17 18 #類的設(shè)計(jì)者,輕松的擴(kuò)展了功能,而類的使用者完全不需要改變自己的代碼 19 class Room: 20 def __init__(self,name,owner,width,length,high): 21 self.name=name 22 self.owner=owner 23 self.__width=width 24 self.__length=length 25 self.__high=high 26 def tell_area(self): #對(duì)外提供的接口,隱藏內(nèi)部實(shí)現(xiàn),此時(shí)我們想求的是體積,內(nèi)部邏輯變了,只需求修該下列一行就可以很簡(jiǎn)答的實(shí)現(xiàn),而且外部調(diào)用感知不到,仍然使用該方法,但是功能已經(jīng)變了 27 return self.__width * self.__length * self.__high 28 29 30 #對(duì)于仍然在使用tell_area接口的人來(lái)說(shuō),根本無(wú)需改動(dòng)自己的代碼,就可以用上新功能 31 >>> r1.tell_area() 私有的屬性property屬性
什么是特性property
property是一種特殊的屬性,訪問(wèn)它時(shí)會(huì)執(zhí)行一段功能(函數(shù))然后返回值
1 例一:BMI指數(shù)(bmi是計(jì)算而來(lái)的,但很明顯它聽(tīng)起來(lái)像是一個(gè)屬性而非方法,如果我們將其做成一個(gè)屬性,更便于理解) 2 3 成人的BMI數(shù)值: 4 過(guò)輕:低于18.5 5 正常:18.5-23.9 6 過(guò)重:24-27 7 肥胖:28-32 8 非常肥胖, 高于32 9 體質(zhì)指數(shù)(BMI)=體重(kg)÷身高^(guò)2(m) 10 EX:70kg÷(1.75×1.75)=22.86 11 12 13 class People: 14 def __init__(self,name,weight,height): 15 self.name=name 16 self.weight=weight 17 self.height=height 18 @property 19 def bmi(self): 20 return self.weight / (self.height**2) 測(cè)試bmi值 1 mport math 2 class Circle: 3 def __init__(self,radius): #圓的半徑radius 4 self.radius=radius 5 6 @property 7 def area(self): 8 return math.pi * self.radius**2 #計(jì)算面積 9 10 @property 11 def perimeter(self): 12 return 2*math.pi*self.radius #計(jì)算周長(zhǎng) 13 14 c=Circle(10) 15 print(c.radius) 16 print(c.area) #可以向訪問(wèn)數(shù)據(jù)屬性一樣去訪問(wèn)area,會(huì)觸發(fā)一個(gè)函數(shù)的執(zhí)行,動(dòng)態(tài)計(jì)算出一個(gè)值 17 print(c.perimeter) #同上 18 ''' 19 輸出結(jié)果: 20 314.1592653589793 21 62.83185307179586 22 ''' 圓的周長(zhǎng)和面積為什么要用property
將一個(gè)類的函數(shù)定義成特性以后,對(duì)象再去使用的時(shí)候obj.name,根本無(wú)法察覺(jué)自己的name是執(zhí)行了一個(gè)函數(shù)然后計(jì)算出來(lái)的,這種特性的使用方式遵循了統(tǒng)一訪問(wèn)的原則
ps:面向?qū)ο蟮姆庋b有三種方式:【public】
這種其實(shí)就是不封裝,是對(duì)外公開(kāi)的
【protected】
這種封裝方式對(duì)外不公開(kāi),
但對(duì)朋友(friend)或者子類(形象的說(shuō)法是“兒子”,但我不知道為什么大家 不說(shuō)“女兒”,
就像“parent”本來(lái)是“父母”的意思,但中文都是叫“父類”)公開(kāi)
【private】
這種封裝對(duì)誰(shuí)都不公開(kāi)
python并沒(méi)有在語(yǔ)法上把它們?nèi)齻€(gè)內(nèi)建到自己的class機(jī)制中,
在C++里一般會(huì)將所有的所有的數(shù)據(jù)都設(shè)置為私有的,然后提供set和get方法(接口)去設(shè)置和獲取,
在python中通過(guò)property方法可以實(shí)現(xiàn)
1 class Foo: 2 def __init__(self,val): 3 self.__NAME=val #將所有的數(shù)據(jù)屬性都隱藏起來(lái) 4 5 @property 6 def name(self): 7 return self.__NAME #obj.name訪問(wèn)的是self.__NAME(這也是真實(shí)值的存放位置) 8 9 @name.setter 10 def name(self,value): 11 if not isinstance(value,str): #在設(shè)定值之前進(jìn)行類型檢查 12 raise TypeError('%s must be str' %value) 13 self.__NAME=value #通過(guò)類型檢查后,將值value存放到真實(shí)的位置self.__NAME 14 15 @name.deleter 16 def name(self): 17 raise TypeError('Can not delete') 私有屬性的修改和刪除
一個(gè)靜態(tài)屬性property本質(zhì)就是實(shí)現(xiàn)了get,set,delete三種方法
1 class Classmethod_Demo(): 2 role = 'dog' 3 4 @classmethod 5 def func(cls): 6 print(cls.role) 7 8 Classmethod_Demo.func() 類方法 1 class Staticmethod_Demo(): 2 role = 'dog' 3 4 @staticmethod 5 def func(): 6 print("當(dāng)普通方法用") 7 8 Staticmethod_Demo.func() 靜態(tài)方法面向?qū)ο蟮能浖_(kāi)發(fā)
很多人在學(xué)完了python的class機(jī)制之后,遇到一個(gè)生產(chǎn)中的問(wèn)題,還是會(huì)懵逼,這其實(shí)太正常了,因?yàn)槿魏纬绦虻拈_(kāi)發(fā)都是先設(shè)計(jì)后編程,python的class機(jī)制只不過(guò)是一種編程方式,如果你硬要拿著class去和你的問(wèn)題死磕,變得更加懵逼都是分分鐘的事,在以前,軟件的開(kāi)發(fā)相對(duì)簡(jiǎn)單,從任務(wù)的分析到編寫(xiě)程序,再到程序的調(diào)試,可以由一個(gè)人或一個(gè)小組去完成。但是隨著軟件規(guī)模的迅速增大,軟件任意面臨的問(wèn)題十分復(fù)雜,需要考慮的因素太多,在一個(gè)軟件中所產(chǎn)生的錯(cuò)誤和隱藏的錯(cuò)誤、未知的錯(cuò)誤可能達(dá)到驚人的程度,這也不是在設(shè)計(jì)階段就完全解決的。
? ? 所以軟件的開(kāi)發(fā)其實(shí)一整套規(guī)范,我們所學(xué)的只是其中的一小部分,一個(gè)完整的開(kāi)發(fā)過(guò)程,需要明確每個(gè)階段的任務(wù),在保證一個(gè)階段正確的前提下再進(jìn)行下一個(gè)階段的工作,稱之為軟件工程
? ? 面向?qū)ο蟮能浖こ贪ㄏ旅鎺讉€(gè)部:
1.面向?qū)ο蠓治?#xff08;object oriented analysis ,OOA)
? ? 軟件工程中的系統(tǒng)分析階段,要求分析員和用戶結(jié)合在一起,對(duì)用戶的需求做出精確的分析和明確的表述,從大的方面解析軟件系統(tǒng)應(yīng)該做什么,而不是怎么去做。面向?qū)ο蟮姆治鲆凑彰嫦驅(qū)ο蟮母拍詈头椒?#xff0c;在對(duì)任務(wù)的分析中,從客觀存在的事物和事物之間的關(guān)系,貴南出有關(guān)的對(duì)象(對(duì)象的‘特征’和‘技能’)以及對(duì)象之間的聯(lián)系,并將具有相同屬性和行為的對(duì)象用一個(gè)類class來(lái)標(biāo)識(shí)。
? ? 建立一個(gè)能反映這是工作情況的需求模型,此時(shí)的模型是粗略的。
2 面向?qū)ο笤O(shè)計(jì)(object oriented design,OOD)
? ? 根據(jù)面向?qū)ο蠓治鲭A段形成的需求模型,對(duì)每一部分分別進(jìn)行具體的設(shè)計(jì)。
? ? 首先是類的設(shè)計(jì),類的設(shè)計(jì)可能包含多個(gè)層次(利用繼承與派生機(jī)制)。然后以這些類為基礎(chǔ)提出程序設(shè)計(jì)的思路和方法,包括對(duì)算法的設(shè)計(jì)。
? ? 在設(shè)計(jì)階段并不牽涉任何一門(mén)具體的計(jì)算機(jī)語(yǔ)言,而是用一種更通用的描述工具(如偽代碼或流程圖)來(lái)描述
3 面向?qū)ο缶幊?#xff08;object oriented programming,OOP)
? ? 根據(jù)面向?qū)ο笤O(shè)計(jì)的結(jié)果,選擇一種計(jì)算機(jī)語(yǔ)言把它寫(xiě)成程序,可以是python
4 面向?qū)ο鬁y(cè)試(object oriented test,OOT)
? ? 在寫(xiě)好程序后交給用戶使用前,必須對(duì)程序進(jìn)行嚴(yán)格的測(cè)試,測(cè)試的目的是發(fā)現(xiàn)程序中的錯(cuò)誤并修正它。
? ? 面向?qū)Φ臏y(cè)試是用面向?qū)ο蟮姆椒ㄟM(jìn)行測(cè)試,以類作為測(cè)試的基本單元。
5 面向?qū)ο缶S護(hù)(object oriendted soft maintenance,OOSM)
? ? 正如對(duì)任何產(chǎn)品都需要進(jìn)行售后服務(wù)和維護(hù)一樣,軟件在使用時(shí)也會(huì)出現(xiàn)一些問(wèn)題,或者軟件商想改進(jìn)軟件的性能,這就需要修改程序。
? ? 由于使用了面向?qū)ο蟮姆椒ㄩ_(kāi)發(fā)程序,使用程序的維護(hù)比較容易。
? ? 因?yàn)閷?duì)象的封裝性,修改一個(gè)對(duì)象對(duì)其他的對(duì)象影響很小,利用面向?qū)ο蟮姆椒ňS護(hù)程序,大大提高了軟件維護(hù)的效率,可擴(kuò)展性高。
?
? ? 在面向?qū)ο蠓椒ㄖ?#xff0c;最早發(fā)展的肯定是面向?qū)ο缶幊?OOP),那時(shí)OOA和OOD都還沒(méi)有發(fā)展起來(lái),因此程序設(shè)計(jì)者為了寫(xiě)出面向?qū)ο蟮某绦?#xff0c;還必須深入到分析和設(shè)計(jì)領(lǐng)域,尤其是設(shè)計(jì)領(lǐng)域,那時(shí)的OOP實(shí)際上包含了現(xiàn)在的OOD和OOP兩個(gè)階段,這對(duì)程序設(shè)計(jì)者要求比較高,許多人感到很難掌握。
? ? 現(xiàn)在設(shè)計(jì)一個(gè)大的軟件,是嚴(yán)格按照面向?qū)ο筌浖こ痰?個(gè)階段進(jìn)行的,這個(gè)5個(gè)階段的工作不是由一個(gè)人從頭到尾完成的,而是由不同的人分別完成,這樣OOP階段的任務(wù)就比較簡(jiǎn)單了。程序編寫(xiě)者只需要根據(jù)OOd提出的思路,用面向?qū)ο笳Z(yǔ)言編寫(xiě)出程序既可。
? ? 在一個(gè)大型軟件開(kāi)發(fā)過(guò)程中,OOP只是很小的一個(gè)部分。
? ? 對(duì)于全棧開(kāi)發(fā)的你來(lái)說(shuō),這五個(gè)階段都有了,對(duì)于簡(jiǎn)單的問(wèn)題,不必嚴(yán)格按照這個(gè)5個(gè)階段進(jìn)行,往往由程序設(shè)計(jì)者按照面向?qū)ο蟮姆椒ㄟM(jìn)行程序設(shè)計(jì),包括類的設(shè)計(jì)和程序的設(shè)計(jì)
幾個(gè)概念的說(shuō)明
?
1.面向?qū)ο蟮某绦蛟O(shè)計(jì)看起來(lái)高大上,所以我在編程時(shí)就應(yīng)該保證通篇class,這樣寫(xiě)出的程序一定是好的程序(面向?qū)ο笾贿m合那些可擴(kuò)展性要求比較高的場(chǎng)景)
2.很多人喜歡說(shuō)面向?qū)ο笕筇匦?#xff08;這是從哪傳出來(lái)的,封裝,多態(tài),繼承?漏洞太多太多,好吧暫且稱為三大特性),那么我在基于面向?qū)ο缶幊虝r(shí),我一定要讓我定義的類中完整的包含這三種特性,這樣寫(xiě)肯定是好的程序
好家伙,我說(shuō)降龍十八掌有十八掌,那么你每次跟人干仗都要從第一掌打到第18掌這才顯得你會(huì)了是么:面對(duì)敵人,你打到第三掌對(duì)方就已經(jīng)倒下了,你說(shuō),不行,你給老子起來(lái),老子還沒(méi)有show完...
3.類有類屬性,實(shí)例有實(shí)例屬性,所以我們?cè)诙xclass時(shí)一定要定義出那么幾個(gè)類屬性,想不到怎么辦,那就使勁的想,定義的越多越牛逼
這就犯了一個(gè)嚴(yán)重的錯(cuò)誤,程序越早面向?qū)ο?#xff0c;死的越早,為啥面向?qū)ο?#xff0c;因?yàn)槲覀円獙?shù)據(jù)與功能結(jié)合到一起,程序整體的結(jié)構(gòu)都沒(méi)有出來(lái),或者說(shuō)需要考慮的問(wèn)題你都沒(méi)有搞清楚個(gè)八九不離十,你就開(kāi)始面向?qū)ο罅?#xff0c;這就導(dǎo)致了,你在那里干想,自以為想通了,定義了一堆屬性,結(jié)果后來(lái)又都用不到,或者想不通到底應(yīng)該定義啥,那就一直想吧,想著想著就瘋了。
你見(jiàn)過(guò)哪家公司要開(kāi)發(fā)一個(gè)軟件,上來(lái)就開(kāi)始寫(xiě),肯定是頻繁的開(kāi)會(huì)討論計(jì)劃,請(qǐng)看第八節(jié)。
面向?qū)ο蟪S眯g(shù)語(yǔ)
抽象/實(shí)現(xiàn)
抽象指對(duì)現(xiàn)實(shí)世界問(wèn)題和實(shí)體的本質(zhì)表現(xiàn),行為和特征建模,建立一個(gè)相關(guān)的子集,可以用于 繪程序結(jié)構(gòu),從而實(shí)現(xiàn)這種模型。抽象不僅包括這種模型的數(shù)據(jù)屬性,還定義了這些數(shù)據(jù)的接口。
對(duì)某種抽象的實(shí)現(xiàn)就是對(duì)此數(shù)據(jù)及與之相關(guān)接口的現(xiàn)實(shí)化(realization)。現(xiàn)實(shí)化這個(gè)過(guò)程對(duì)于客戶 程序應(yīng)當(dāng)是透明而且無(wú)關(guān)的。?
封裝/接口
封裝描述了對(duì)數(shù)據(jù)/信息進(jìn)行隱藏的觀念,它對(duì)數(shù)據(jù)屬性提供接口和訪問(wèn)函數(shù)。通過(guò)任何客戶端直接對(duì)數(shù)據(jù)的訪問(wèn),無(wú)視接口,與封裝性都是背道而馳的,除非程序員允許這些操作。作為實(shí)現(xiàn)的 一部分,客戶端根本就不需要知道在封裝之后,數(shù)據(jù)屬性是如何組織的。在Python中,所有的類屬性都是公開(kāi)的,但名字可能被“混淆”了,以阻止未經(jīng)授權(quán)的訪問(wèn),但僅此而已,再?zèng)]有其他預(yù)防措施了。這就需要在設(shè)計(jì)時(shí),對(duì)數(shù)據(jù)提供相應(yīng)的接口,以免客戶程序通過(guò)不規(guī)范的操作來(lái)存取封裝的數(shù)據(jù)屬性。
注意:封裝絕不是等于“把不想讓別人看到、以后可能修改的東西用private隱藏起來(lái)”
真正的封裝是,經(jīng)過(guò)深入的思考,做出良好的抽象,給出“完整且最小”的接口,并使得內(nèi)部細(xì)節(jié)可以對(duì)外透明
(注意:對(duì)外透明的意思是,外部調(diào)用者可以順利的得到自己想要的任何功能,完全意識(shí)不到內(nèi)部細(xì)節(jié)的存在)
合成
合成擴(kuò)充了對(duì)類的 述,使得多個(gè)不同的類合成為一個(gè)大的類,來(lái)解決現(xiàn)實(shí)問(wèn)題。合成 述了 一個(gè)異常復(fù)雜的系統(tǒng),比如一個(gè)類由其它類組成,更小的組件也可能是其它的類,數(shù)據(jù)屬性及行為, 所有這些合在一起,彼此是“有一個(gè)”的關(guān)系。
派生/繼承/繼承結(jié)構(gòu)
派生描述了子類衍生出新的特性,新類保留已存類類型中所有需要的數(shù)據(jù)和行為,但允許修改或者其它的自定義操作,都不會(huì)修改原類的定義。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結(jié)構(gòu)表示多“代”派生,可以述成一個(gè)“族譜”,連續(xù)的子類,與祖先類都有關(guān)系。
泛化/特化
基于繼承
泛化表示所有子類與其父類及祖先類有一樣的特點(diǎn)。
特化描述所有子類的自定義,也就是,什么屬性讓它與其祖先類不同。
多態(tài)與多態(tài)性
多態(tài)指的是同一種事物的多種狀態(tài):水這種事物有多種不同的狀態(tài):冰,水蒸氣
多態(tài)性的概念指出了對(duì)象如何通過(guò)他們共同的屬性和動(dòng)作來(lái)操作及訪問(wèn),而不需考慮他們具體的類。
冰,水蒸氣,都繼承于水,它們都有一個(gè)同名的方法就是變成云,但是冰.變?cè)?),與水蒸氣.變?cè)?)是截然不同的過(guò)程,雖然調(diào)用的方法都一樣
自省/反射
自省也稱作反射,這個(gè)性質(zhì)展示了某對(duì)象是如何在運(yùn)行期取得自身信息的。如果傳一個(gè)對(duì)象給你,你可以查出它有什么能力,這是一項(xiàng)強(qiáng)大的特性。如果Python不支持某種形式的自省功能,dir和type內(nèi)建函數(shù),將很難正常工作。還有那些特殊屬性,像__dict__,__name__及__doc__
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/panfb/p/7852483.html
總結(jié)
以上是生活随笔為你收集整理的23 Python 面向对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GitLab搭建详细过程
- 下一篇: Xcode9学习笔记63 - 使用Sys