1.1面向?qū)ο?br>
面向?qū)ο?object-oriented ;簡稱: OO)至今還沒有統(tǒng)一的概念 我這里把它定義為:按人們 認(rèn)識客觀世界的系統(tǒng)思維方式,采用基于對象(實(shí)體)的概念建立模型,模擬客觀世界分析琐凭、設(shè) 計、實(shí)現(xiàn)軟件的辦法浊服。
面向?qū)ο缶幊?Object Oriented Programming-OOP)是一種解決軟件復(fù)用的設(shè)計和編程方法。 這種方法把軟件系統(tǒng)中相近相似的操作邏輯和操作 應(yīng)用數(shù)據(jù)胚吁、狀態(tài),以類的型式描述出來,以對象實(shí)例的形式在軟件系統(tǒng)中復(fù)用,以達(dá)到提高軟件開發(fā)效率的作用牙躺。
面向?qū)ο蟮睦斫猓?/p>
面向?qū)ο笫且环N設(shè)計思想
1.符合人們的思考習(xí)慣
2.把執(zhí)行者變成指揮者
3.簡化功能,把復(fù)雜的事情簡單化
面向?qū)ο笥腥筇卣鳎?/p>
1.封裝
2.繼承
3.多態(tài)
1.2類和對象
面向?qū)ο缶幊痰?個非常重要的概念:類和對象
對象是面向?qū)ο缶幊痰暮诵耐蠓觯谑褂脤ο蟮倪^程中孽拷,為了將具有共同特征和行為的一組對象抽象定義,提出了另外一個新的概念——類
類就相當(dāng)于制造飛機(jī)時的圖紙半抱,用它來進(jìn)行創(chuàng)建的飛機(jī)就相當(dāng)于對象
類是對事務(wù)的描述脓恕,是抽象的。
對象是類的具體體現(xiàn)窿侈。
1.2.1 ?類
人以類聚物以群分炼幔。
具有相似內(nèi)部狀態(tài)和運(yùn)動規(guī)律的實(shí)體的集合(或統(tǒng)稱為抽象)。
具有相同屬性和行為事物的統(tǒng)稱
類是抽象的,在使用的時候通常會找到這個類的一個具體的存在,使用這個具體的存在史简。一個類可以找到多個對象
1.2.2 對象
某一個具體事物的存在,在現(xiàn)實(shí)世界中可以是看得見摸得著的乃秀。
可以是直接使用的
1.2.3 類和對象的關(guān)系
類>=對象
1.2.4 練習(xí)區(qū)分類和對象
奔馳汽車類
奔馳smart類
張三的那輛奔馳smart對象
狗類
大黃狗類
李四家那只大黃狗對象
水果類
蘋果類
紅蘋果類紅富士蘋果類
我嘴里吃了一半的蘋果對象
1.2.5 類的構(gòu)成
類(Class)由3個部分構(gòu)成
·類的名稱:類名
·類的屬性:一組數(shù)據(jù)成員變量
·類的方法:允許對進(jìn)行操作的方法(行為)成員方法
舉例:
1)人類設(shè)計,只關(guān)心3樣?xùn)|西:
·事物名稱(類名):人(Person)
·屬性:身高(height)、年齡(age)
·方法(行為/功能):跑(run)圆兵、打架(fight)
2)狗類的設(shè)計
·類名:狗(Dog)
·屬性:品種 跺讯、毛色、性別殉农、名字刀脏、 腿兒的數(shù)量
·方法(行為/功能):叫 、跑超凳、咬人愈污、吃耀态、搖尾巴
1.2.6 類的抽象
如何把日常生活中的事物抽象成程序中的類?
擁有相同(或者類似)屬性和行為的對象都可以抽像出一個類
方法:一般名詞都是類(名詞提煉法)
<1>坦克發(fā)射3顆炮彈轟掉了2架飛機(jī)
·坦克--》可以抽象成 類
·炮彈--》可以抽象成類
·飛機(jī)-》可以抽象成類
<2>小明在公車上牽著一條叼著熱狗的狗
·小明--》 人類
·公車--》 交通工具類
·熱狗--》 食物類
·狗--》 狗類
1.3 定義類
定義一個類,格式如下:
class類名:
方法列表
demo:定義一個Car類
#定義類
classCar:
#方法
defgetCarInfo(self):
print('車輪子個數(shù):%d,顏色%s'%(self.wheelNum, self.color))
defmove(self):
print("車正在移動...")
說明:
·定義類時有2種:新式類和經(jīng)典類钙畔,上面的Car為經(jīng)典類茫陆,如果是Car(object)則為新式類
·類名的命名規(guī)則按照"大駝峰"
1.4 ?創(chuàng)建對象
通過上一節(jié)課程,定義了一個Car類擎析;就好比有車一個張圖紙簿盅,那么接下來就應(yīng)該把圖紙交給生成工人們?nèi)ド闪?/p>
python中,可以根據(jù)已經(jīng)定義的類去創(chuàng)建出一個個對象
創(chuàng)建對象的格式為:
對象名=類名()
創(chuàng)建對象demo:
#定義類
classCar:
#移動
defmove(self):
print('車在奔跑...')
#鳴笛
deftoot(self):
print("車在鳴笛...嘟嘟..")
#創(chuàng)建一個對象揍魂,并用變量BMW來保存它的引用
BMW = Car()
BMW.color ='黑色'
BMW.wheelNum =4#輪子數(shù)量
BMW.move()
BMW.toot()
print(BMW.color)
print(BMW.wheelNum)
總結(jié):
·BMW = Car()桨醋,這樣就產(chǎn)生了一個Car的實(shí)例對象,此時也可以通過實(shí)例對象BMW來訪問屬性或者方法
·第一次使用BMW.color = '黑色'表示給BMW這個對象添加屬性现斋,如果后面再次出現(xiàn)BMW.color = xxx表示對屬性進(jìn)行修改
·BMW是一個對象喜最,它擁有屬性(數(shù)據(jù))和方法(函數(shù))
·當(dāng)創(chuàng)建一個對象時,就是用一個模子庄蹋,來制造一個實(shí)物
1.5 __init__()方法
在上一小節(jié)的demo中瞬内,我們已經(jīng)給BMW這個對象添加了2個屬性,wheelNum(車的輪胎數(shù)量)以及color(車的顏色)限书,試想如果再次創(chuàng)建一個對象的話虫蝶,肯定也需要進(jìn)行添加屬性,顯然這樣做很費(fèi)事倦西,那么有沒有辦法能夠在創(chuàng)建對象的時候能真,就順便把車這個對象的屬性給設(shè)置呢?
答:__init__()方法
在java里叫構(gòu)造方法
1.5.1 使用方法
def類名:
#初始化函數(shù)扰柠,用來完成一些默認(rèn)的設(shè)定
def__init__():
pass
1.5.2 __init__()方法的調(diào)用
#定義汽車類
classCar:
def__init__(self):
self.wheelNum =4
self.color ='藍(lán)色'
defmove(self):
print('車在跑粉铐,目標(biāo):夏威夷')
#創(chuàng)建對象
BMW = Car()
print('車的顏色為:%s'%BMW.color)
print('車輪胎數(shù)量為:%d'%BMW.wheelNum)
1.5.3 總結(jié)
當(dāng)創(chuàng)建Car對象后,在沒有調(diào)用__init__()方法的前提下卤档,BMW就默認(rèn)擁有了2個屬性wheelNum和color蝙泼,原因是__init__()方法是在創(chuàng)建對象后,就立刻被默認(rèn)調(diào)用了
想一想
既然在創(chuàng)建完對象后__init__()方法已經(jīng)被默認(rèn)的執(zhí)行了裆装,那么能否讓對象在調(diào)用__init__()方法的時候傳遞一些參數(shù)呢踱承?如果可以,那怎樣傳遞呢哨免?
#定義汽車類
classCar:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
defmove(self):
print('車在跑茎活,目標(biāo):夏威夷')
#創(chuàng)建對象
BMW = Car(4,'green')
print('車的顏色為:%s'%BMW.color)
print('車輪子數(shù)量為:%d'%BMW.wheelNum)
1.5.4 ?總結(jié)2
·__init__()方法,在創(chuàng)建一個對象時默認(rèn)被調(diào)用琢唾,不需要手動調(diào)用
·__init__(self)中载荔,默認(rèn)有1個參數(shù)名字為self,如果在創(chuàng)建對象時傳遞了2個實(shí)參采桃,那么__init__(self)中出了self作為第一個形參外還需要2個形參懒熙,例如__init__(self,x,y)
·__init__(self)中的self參數(shù)丘损,不需要開發(fā)者傳遞,python解釋器會自動把當(dāng)前的對象引用傳遞進(jìn)去
1.6 應(yīng)用:創(chuàng)建多個對象
·根據(jù)上兩節(jié)創(chuàng)建一個Car類
·創(chuàng)建出多個汽車對象工扎,比如BMW徘钥、AUDI等
1.7 ‘魔法方法’
1.7.1 定義__str__()的方法
classCar:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def__str__(self):
msg ="嘿。肢娘。呈础。我的顏色是"+ self.color +"我有"+ int(self.wheelNum) +"個輪胎..."
returnmsg
defmove(self):
print('車在跑,目標(biāo):夏威夷')
BMW = Car(4,"白色")
print(BMW)
1.7.2 總結(jié)
·在python中方法名如果是__xxxx__()的橱健,那么就有特殊的功能而钞,因此叫做“魔法”方法
·當(dāng)使用print輸出對象的時候,只要自己定義了__str__(self)方法拘荡,那么就會打印從在這個方法中return的數(shù)據(jù)
1.8.1 理解self
看如下示例:
#定義一個類
classAnimal:
#方法
def__init__(self, name):
self.name = name
defprintName(self):
print('名字為:%s'%self.name)
#定義一個函數(shù)
defmyPrint(animal):
animal.printName()
dog1 = Animal('西西')
myPrint(dog1)
dog2 = Animal('北北')
myPrint(dog2)
1.8.2 總結(jié)
·所謂的self臼节,可以理解為自己
·可以把self當(dāng)做C++中類里面的this指針一樣理解,就是對象自身的意思
·某個對象調(diào)用其方法時珊皿,python解釋器會把這個對象作為第一個參數(shù)傳遞給self网缝,所以開發(fā)者只需要傳遞后面的參數(shù)即可
1.9 應(yīng)用:烤地瓜
1.9.1 分析烤地瓜的屬性和方法
示例屬性如下:
·cookedLevel :這是數(shù)字;0~3表示還是生的蟋定,超過3表示半生不熟途凫,超過5表示已經(jīng)烤好了,超過8表示已經(jīng)烤成木炭了溢吻!我們的地瓜開始時時生的
·cookedString :這是字符串;描述地瓜的生熟程度
·condiments :這是地瓜的配料列表果元,比如番茄醬促王、芥末醬等
示例方法如下:
·cook():把地瓜烤一段時間
·addCondiments():給地瓜添加配料
·__init__():設(shè)置默認(rèn)的屬性
·__str__():讓print的結(jié)果看起來更好一些
1.9.2定義類,并且定義__init__()方法
#定義`地瓜`類
classSweetPotato:
'這是烤地瓜的類'
#定義初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
1.9.3 添加‘烤地瓜’的方法
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
1.9.4 測試
把上面2塊代碼合并為一個程序后而晒,在代碼的下面添加以下代碼進(jìn)行測試
mySweetPotato = SweetPotato()
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
完整的代碼為:
classSweetPotato:
'這是烤地瓜的類'
#定義初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
#用來進(jìn)行測試
mySweetPotato = SweetPotato()
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
1.9.5 測試cook的方法是否好用
在上面的代碼最后面添加如下代碼:
print("------接下來要進(jìn)行烤地瓜了-----")
mySweetPotato.cook(4)#烤4分鐘
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
1.9.6 定義addCondiments()方法和__str__()方法
def__str__(self):
msg = self.cookedString +"地瓜"
iflen(self.condiments) >0:
msg = msg +"("
fortempinself.condiments:
msg = msg + temp +", "
msg = msg.strip(", ")
msg = msg +")"
returnmsg
defaddCondiments(self, condiments):
self.condiments.append(condiments)
1.9.7 ?再次測試
完整的代碼如下:
classSweetPotato:
"這是烤地瓜的類"
#定義初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
#定制print時的顯示內(nèi)容
def__str__(self):
msg = self.cookedString +"地瓜"
iflen(self.condiments) >0:
msg = msg +"("
fortempinself.condiments:
msg = msg + temp +", "
msg = msg.strip(", ")
msg = msg +")"
returnmsg
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
#添加配料
defaddCondiments(self, condiments):
self.condiments.append(condiments)
#用來進(jìn)行測試
mySweetPotato = SweetPotato()
print("------有了一個地瓜蝇狼,還沒有烤-----")
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
print("------接下來要進(jìn)行烤地瓜了-----")
print("------地瓜經(jīng)烤了4分鐘-----")
mySweetPotato.cook(4)#烤4分鐘
print(mySweetPotato)
print("------地瓜又經(jīng)烤了3分鐘-----")
mySweetPotato.cook(3)#又烤了3分鐘
print(mySweetPotato)
print("------接下來要添加配料-番茄醬------")
mySweetPotato.addCondiments("番茄醬")
print(mySweetPotato)
print("------地瓜又經(jīng)烤了5分鐘-----")
mySweetPotato.cook(5)#又烤了5分鐘
print(mySweetPotato)
print("------接下來要添加配料-芥末醬------")
mySweetPotato.addCondiments("芥末醬")
print(mySweetPotato)
1.10 應(yīng)用:存放家具
#定義一個home類
classHome:
def__init__(self, area):
self.area = area#房間剩余的可用面積
#self.light = 'on' #燈默認(rèn)是亮的
self.containsItem = []
def__str__(self):
msg ="當(dāng)前房間可用面積為:"+ str(self.area)
iflen(self.containsItem) >0:
msg = msg +"容納的物品有: "
fortempinself.containsItem:
msg = msg + temp.getName() +", "
msg = msg.strip(", ")
returnmsg
#容納物品
defaccommodateItem(self,item):
#如果可用面積大于物品的占用面積
needArea = item.getUsedArea()
ifself.area > needArea:
self.containsItem.append(item)
self.area -= needArea
print("ok:已經(jīng)存放到房間中")
else:
print("err:房間可用面積為:%d,但是當(dāng)前要存放的物品需要的面積為%d"%(self.area, needArea))
#定義bed類
classBed:
def__init__(self,area,name ='床'):
self.name = name
self.area = area
def__str__(self):
msg ='床的面積為:'+ str(self.area)
returnmsg
#獲取床的占用面積
defgetUsedArea(self):
returnself.area
defgetName(self):
returnself.name
#創(chuàng)建一個新家對象
newHome = Home(100)#100平米
print(newHome)
#創(chuàng)建一個床對象
newBed = Bed(20)
print(newBed)
#把床安放到家里
newHome.accommodateItem(newBed)
print(newHome)
#創(chuàng)建一個床對象
newBed2 = Bed(30,'席夢思')
print(newBed2)
#把床安放到家里
newHome.accommodateItem(newBed2)
print(newHome)
總結(jié):
·如果一個對象與另外一個對象有一定的關(guān)系,那么一個對象可用是另外一個對象的屬性
思維升華:
·添加“開倡怎、關(guān)”燈迅耘,讓房間、床一起亮监署、滅
1.11 保護(hù)對象的屬性
如果有一個對象颤专,當(dāng)需要對其進(jìn)行修改屬性時,有2種方法
·對象名.屬性名=數(shù)據(jù)---->直接修改
·對象名.方法名() ---->間接修改
為了更好的保存屬性安全钠乏,即不能隨意修改栖秕,一般的處理方式為
·將屬性定義為私有屬性
·添加一個可以調(diào)用的方法,供調(diào)用
classPeople(object):
def__init__(self, name):
self.__name = name
defgetName(self):
returnself.__name
defsetName(self, newName):
iflen(newName) >=5:
self.__name = newName
else:
print("error:名字長度需要大于或者等于5")
xiaoming = People("xx")
print(xiaoming.__name)
classPeople(object):
def__init__(self, name):
self.__name = name
defgetName(self):
returnself.__name
defsetName(self, newName):
iflen(newName) >=5:
self.__name = newName
else:
print("error:名字長度需要大于或者等于5")
xiaoming = People("xx")
xiaoming.setName("yy")
print(xiaoming.getName())
xiaoming.setName("lisi")
print(xiaoming.getName())
總結(jié)
·Python中沒有像C++中public和private這些關(guān)鍵字來區(qū)別公有屬性和私有屬性
·它是以屬性命名方式來區(qū)分晓避,如果在屬性名前面加了2個下劃線'__'簇捍,則表明該屬性是私有屬性只壳,否則為公有屬性(方法也是一樣,方法名前面加了2個下劃線的話表示該方法是私有的暑塑,否則為公有的)吼句。
1.12 __del__()方法
創(chuàng)建對象后,python解釋器默認(rèn)調(diào)用__init__()方法事格;
當(dāng)刪除一個對象時惕艳,python解釋器也會默認(rèn)調(diào)用一個方法,這個方法為__del__()方法
importtime
classAnimal(object):
#初始化方法
#創(chuàng)建完對象后會自動被調(diào)用
def__init__(self, name):
print('__init__方法被調(diào)用')
self.__name = name
#析構(gòu)方法
#當(dāng)對象被刪除時分蓖,會自動被調(diào)用
def__del__(self):
print("__del__方法被調(diào)用")
print("%s對象馬上被干掉了..."%self.__name)
#創(chuàng)建對象
dog = Animal("哈皮狗")
#刪除對象
deldog
cat = Animal("波斯貓")
cat2 = cat
cat3 = cat
print("---馬上 刪除cat對象")
delcat
print("---馬上 刪除cat2對象")
delcat2
print("---馬上 刪除cat3對象")
delcat3
print("程序2秒鐘后結(jié)束")
time.sleep(2)
總結(jié)
·當(dāng)有1個變量保存了對象的引用時尔艇,此對象的引用計數(shù)就會加1
·當(dāng)使用del刪除變量指向的對象時,如果對象的引用計數(shù)不會1么鹤,比如3终娃,那么此時只會讓這個引用計數(shù)減1,即變?yōu)?蒸甜,當(dāng)再次調(diào)用del時棠耕,變?yōu)?,如果再調(diào)用1次del柠新,此時會真的把對象進(jìn)行刪除,__del__會被調(diào)用
1.13繼承
將共性的內(nèi)容放在父類中窍荧,子類只需要關(guān)注自己特有的內(nèi)容。在程序中恨憎,繼承描述的是事物之間的所屬關(guān)系蕊退,例如貓和狗都屬于動物,程序中便可以描述為貓和狗繼承自動物憔恳;同理瓤荔,波斯貓和巴厘貓都繼承自貓,而沙皮狗和斑點(diǎn)狗都繼承自狗钥组。
1.131 繼承的實(shí)例
lassCat(object):
def__init__(self, name, color="白色"):
self.name = name
self.color = color
defrun(self):
print("%s--在跑"%self.name)
#定義一個子類输硝,繼承Cat類如下:
classBosi(Cat):
defsetNewName(self, newName):
self.name = newName
defeat(self):
print("%s--在吃"%self.name)
bs = Bosi("印度貓")
print('bs的名字為:%s'%bs.name)
print('bs的顏色為:%s'%bs.color)
bs.eat()
bs.setNewName('波斯')
bs.run()
說明:
·雖然子類沒有定義__init__方法,但是父類有程梦,所以在子類繼承父類的時候這個方法就被繼承了点把,所以只要創(chuàng)建Bosi的對象,就默認(rèn)執(zhí)行了那個繼承過來的__init__方法
總結(jié)
·子類在繼承的時候屿附,在定義類時郎逃,小括號()中為父類的名字
·父類的屬性、方法挺份,會被繼承給子類
1.132注意點(diǎn)
classAnimal(object):
def__init__(self, name='動物', color='白色'):
self.__name = name
self.color = color
def__test(self):
print(self.__name)
print(self.color)
deftest(self):
print(self.__name)
print(self.color)
classDog(Animal):
defdogTest1(self):
#print(self.__name) #不能訪問到父類的私有屬性
print(self.color)
defdogTest2(self):
#self.__test() #不能訪問父類中的私有方法
self.test()
A = Animal()
#print(A.__name) #程序出現(xiàn)異常衣厘,不能訪問私有屬性
print(A.color)
#A.__test() #程序出現(xiàn)異常,不能訪問私有方法
A.test()
print("------分割線-----")
D = Dog(name ="小花狗", color ="黃色")
D.dogTest1()
D.dogTest2()
總結(jié)
·私有的屬性,不能通過對象直接訪問影暴,但是可以通過方法訪問
·私有的方法错邦,不能通過對象直接訪問
·私有的屬性、方法型宙,不會被子類繼承撬呢,也不能被訪問
·一般情況下,私有的屬性妆兑、方法都是不對外公布的魂拦,往往用來做內(nèi)部的事情,起到安全的作用
1.14多繼承
Python中多繼承的格式如下:
#定義一個父類
classA:
defprintA(self):
print('----A----')
#定義一個父類
classB:
defprintB(self):
print('----B----')
#定義一個子類搁嗓,繼承自A芯勘、B
classC(A,B):
defprintC(self):
print('----C----')
obj_C = C()
obj_C.printA()
obj_C.printB()
運(yùn)行結(jié)果:
----A----
----B----
說明
·python中是可以多繼承的,在java中叫接口
·父類中的方法腺逛、屬性荷愕,子類會繼承
注意點(diǎn)
·想一想:
如果在上面的多繼承例子中,如果父類A和父類B中棍矛,有一個同名的方法安疗,那么通過子類去調(diào)用的時候,調(diào)用哪個够委?
#coding=utf-8
classbase(object):
deftest(self):
print('----base test----')
classA(base):
deftest(self):
print('----A test----')
#定義一個父類
classB(base):
deftest(self):
print('----B test----')
#定義一個子類荐类,繼承自A、B
classC(A,B):
pass
obj_C = C()
obj_C.test()
print(C.__mro__)#可以查看C類的對象搜索方法時的先后順序
1.15重寫父類與調(diào)用父類方法
1.151重寫父類的方法
所謂重寫茁帽,就是子類中玉罐,有一個和父類相同名字的方法,在子類中的方法會覆蓋掉父類中同名的方法
#coding=utf-8
classCat(object):
defsayHello(self):
print("halou-----1")
classBosi(Cat):
defsayHello(self):
print("halou-----2")
bosi = Bosi()
bosi.sayHello()
1.15.2調(diào)用父類的方法
#coding=utf-8
classCat(object):
def__init__(self,name):
self.name = name
self.color ='yellow'
classBosi(Cat):
def__init__(self,name):
#調(diào)用父類的__init__方法1(python2)
#Cat.__init__(self,name)
#調(diào)用父類的__init__方法2
#super(Bosi,self).__init__(name)
#調(diào)用父類的__init__方法3
super().__init__(name)
defgetName(self):
returnself.name
bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)
1.6多態(tài)
多態(tài)的概念是應(yīng)用于Java和C#這一類強(qiáng)類型語言中潘拨,而Python崇尚“鴨子類型”厌小。
首先Python不支持多態(tài),也不用支持多態(tài)战秋,python是一種多態(tài)語言,崇尚鴨子類型讨韭。以下是維基百科中對鴨子類型得論述:
在程序設(shè)計中脂信,鴨子類型(英語:duck typing)是動態(tài)類型的一種風(fēng)格。在這種風(fēng)格中透硝,一個對象有效的語義狰闪,不是由繼承自特定的類或?qū)崿F(xiàn)特定的接口,而是由當(dāng)前方法和屬性的集合決定濒生。這個概念的名字來源于由James Whitcomb Riley提出的鴨子測試埋泵,“鴨子測試”可以這樣表述:
“當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子丽声,那么這只鳥就可以被稱為鴨子礁蔗。”
在鴨子類型中雁社,關(guān)注的不是對象的類型本身浴井,而是它是如何使用的。例如霉撵,在不使用鴨子類型的語言中磺浙,我們可以編寫一個函數(shù),它接受一個類型為鴨的對象徒坡,并調(diào)用它的走和叫方法撕氧。在使用鴨子類型的語言中以政,這樣的一個函數(shù)可以接受一個任意類型的對象捐寥,并調(diào)用它的走和叫方法淋淀。如果這些需要被調(diào)用的方法不存在滤灯,那么將引發(fā)一個運(yùn)行時錯誤旁理。任何擁有這樣的正確的走和叫方法的對象都可被函數(shù)接受的這種行為引出了以上表述买猖,這種決定類型的方式因此得名撤师。
鴨子類型通常得益于不測試方法和函數(shù)中參數(shù)的類型绵脯,而是依賴文檔海洼、清晰的代碼和測試來確保正確使用跨新。從靜態(tài)類型語言轉(zhuǎn)向動態(tài)類型語言的用戶通常試圖添加一些靜態(tài)的(在運(yùn)行之前的)類型檢查,從而影響了鴨子類型的益處和可伸縮性坏逢,并約束了語言的動態(tài)特性域帐。
所謂多態(tài):定義時的類型和運(yùn)行時的類型不一樣,此時就成為多態(tài)
·Python偽代碼實(shí)現(xiàn)Java或C#的多態(tài)
classF1(object):
defshow(self):
print'F1.show'
classS1(F1):
defshow(self):
print'S1.show'
classS2(F1):
defshow(self):
print'S2.show'
#由于在Java或C#中定義函數(shù)參數(shù)時是整,必須指定參數(shù)的類型
#為了讓Func函數(shù)既可以執(zhí)行S1對象的show方法肖揣,又可以執(zhí)行S2對象的show方法,所以浮入,定義了一個S1和S2類的父類
#而實(shí)際傳入的參數(shù)是:S1對象和S2對象
defFunc(F1 obj):
"""Func函數(shù)需要接收一個F1類型或者F1子類的類型"""
printobj.show()
s1_obj = S1()
Func(s1_obj)#在Func函數(shù)中傳入S1類的對象s1_obj龙优,執(zhí)行S1的show方法,結(jié)果:S1.show
s2_obj = S2()
Func(s2_obj)#在Func函數(shù)中傳入Ss類的對象ss_obj事秀,執(zhí)行Ss的show方法彤断,結(jié)果:S2.show
·Python “鴨子類型”
classF1(object):
defshow(self):
print'F1.show'
classS1(F1):
defshow(self):
print'S1.show'
classS2(F1):
defshow(self):
print'S2.show'
defFunc(obj):
printobj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
1.17類屬性、實(shí)例屬性
在了解了類基本的東西之后易迹,下面看一下python中這幾個概念的區(qū)別
先來談一下類屬性和實(shí)例屬性
在前面的例子中我們接觸到的就是實(shí)例屬性(對象屬性)宰衙,顧名思義,類屬性就是類對象所擁有的屬性睹欲,它被所有類對象的實(shí)例對象所共有供炼,在內(nèi)存中只存在一個副本一屋,這個和C++中類的靜態(tài)成員變量有點(diǎn)類似。對于公有的類屬性袋哼,在類外可以通過類對象和實(shí)例對象訪問
1.17.1類屬性
classPeople(object):
name ='Tom'#公有的類屬性
__age =12#私有的類屬性
p = People()
print(p.name)#正確
print(People.name)#正確
print(p.__age)#錯誤冀墨,不能在類外通過實(shí)例對象訪問私有的類屬性
print(People.__age)#錯誤,不能在類外通過類對象訪問私有的類屬性
1.17.2實(shí)例屬性
classPeople(object):
address ='山東'#類屬性
def__init__(self):
self.name ='xiaowang'#實(shí)例屬性
self.age =20#實(shí)例屬性
p = People()
p.age =12#實(shí)例屬性
print(p.address)#正確
print(p.name)#正確
print(p.age)#正確
print(People.address)#正確
print(People.name)#錯誤
print(People.age)#錯誤
1.17.3通過實(shí)例對象修改類屬性
classPeople(object):
country ='china'#類屬性
print(People.country)
p = People()
print(p.country)
p.country ='japan'
print(p.country)#實(shí)例屬性會屏蔽掉同名的類屬性
print(People.country)
delp.country#刪除實(shí)例屬性
print(p.country)
總結(jié)
如果需要在類外修改類屬性先嬉,必須通過類對象去引用然后進(jìn)行修改轧苫。如果通過實(shí)例對象去引用,會產(chǎn)生一個同名的實(shí)例屬性疫蔓,這種方式修改的是實(shí)例屬性含懊,不會影響到類屬性,并且之后如果通過實(shí)例對象去引用該名稱的屬性衅胀,實(shí)例屬性會強(qiáng)制屏蔽掉類屬性岔乔,即引用的是實(shí)例屬性,除非刪除了該實(shí)例屬性滚躯。
1.18類方法和靜態(tài)方法
1.18.1類方法
是類對象所擁有的方法雏门,需要用修飾器@classmethod來標(biāo)識其為類方法,對于類方法掸掏,第一個參數(shù)必須是類對象茁影,一般以cls作為第一個參數(shù)(當(dāng)然可以用其他名稱的變量作為其第一個參數(shù),但是大部分人都習(xí)慣以'cls'作為第一個參數(shù)的名字丧凤,就最好用'cls'了)募闲,能夠通過實(shí)例對象和類對象去訪問。
classPeople(object):
country ='china'
#類方法愿待,用classmethod來進(jìn)行修飾
@classmethod
defgetCountry(cls):
returncls.country
p = People()
printp.getCountry()#可以用過實(shí)例對象引用
printPeople.getCountry()#可以通過類對象引用
類方法還有一個用途就是可以對類屬性進(jìn)行修改:
classPeople(object):
country ='china'
#類方法浩螺,用classmethod來進(jìn)行修飾
@classmethod
defgetCountry(cls):
returncls.country
@classmethod
defsetCountry(cls,country):
cls.country = country
p = People()
printp.getCountry()#可以用過實(shí)例對象引用
printPeople.getCountry()#可以通過類對象引用
p.setCountry('japan')
printp.getCountry()
printPeople.getCountry()
1.18.2靜態(tài)方法
需要通過修飾器@staticmethod來進(jìn)行修飾,靜態(tài)方法不需要多定義參數(shù)
classPeople(object):
country ='china'
@staticmethod
#靜態(tài)方法
defgetCountry():
returnPeople.country
printPeople.getCountry()
總結(jié)
從類方法和實(shí)例方法以及靜態(tài)方法的定義形式就可以看出來仍侥,類方法的第一個參數(shù)是類對象cls要出,那么通過cls引用的必定是類對象的屬性和方法;而實(shí)例方法的第一個參數(shù)是實(shí)例對象self农渊,那么通過self引用的可能是類屬性患蹂、也有可能是實(shí)例屬性(這個需要具體分析),不過在存在相同名稱的類屬性和實(shí)例屬性的情況下砸紊,實(shí)例屬性優(yōu)先級更高传于。靜態(tài)方法中不需要額外定義參數(shù),因此在靜態(tài)方法中引用類屬性的話批糟,必須通過類對象來引用
1.19工廠設(shè)計模型
設(shè)計一個賣車的4S店,該怎樣做呢看铆?
#定義車類
classCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義一個銷售車的店類
classCarStore(object):
deforder(self):
self.car = Car()#找一輛車
self.car.move()
self.car.stop()
說明
上面的4s店徽鼎,只能銷售那一種類型的車
如果這個是個銷售北京現(xiàn)代品牌的車,比如伊蘭特、索納塔等否淤,該怎樣做呢悄但?
設(shè)計一個賣北京現(xiàn)代車的4S店
#定義伊蘭特車類
classYilanteCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義索納塔車類
classSuonataCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義一個銷售北京現(xiàn)代車的店類
classCarStore(object):
deforder(self, typeName):
#根據(jù)客戶的不同要求,生成不同的類型的車
iftypeName =="伊蘭特":
car = YilanteCar()
eliftypeName =="索納塔":
car = SuonataCar()
returncar
這樣做石抡,不太好檐嚣,因為當(dāng)北京現(xiàn)代又生產(chǎn)一種新類型的車時,又得在CarStore類中修改啰扛,有沒有好的解決辦法呢嚎京?
使用函數(shù)實(shí)現(xiàn)
#定義伊蘭特車類
classYilanteCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義索納塔車類
classSuonataCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#用來生成具體的對象
defcreateCar(typeName):
iftypeName =="伊蘭特":
car = YilanteCar()
eliftypeName =="索納塔":
car = SuonataCar()
returncar
#定義一個銷售北京現(xiàn)代車的店類
classCarStore(object):
deforder(self, typeName):
#讓工廠根據(jù)類型,生產(chǎn)一輛汽車
car = createCar(typeName)
returncar
使用類來實(shí)現(xiàn)
#定義伊蘭特車類
classYilanteCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義索納塔車類
classSuonataCar(object):
#定義車的方法
defmove(self):
print("---車在移動---")
defstop(self):
print("---停車---")
#定義一個生產(chǎn)汽車的工廠隐解,讓其根據(jù)具體的訂單生產(chǎn)車
classCarFactory(object):
defcreateCar(self,typeName):
iftypeName =="伊蘭特":
car = YilanteCar()
eliftypeName =="索納塔":
car = SuonataCar()
returncar
#定義一個銷售北京現(xiàn)代車的店類
classCarStore(object):
def__init__(self):
#設(shè)置4s店的指定生產(chǎn)汽車的工廠
self.carFactory = CarFactory()
deforder(self, typeName):
#讓工廠根據(jù)類型鞍帝,生產(chǎn)一輛汽車
car = self.carFactory.createCar(typeName)
returncar
咋一看來,好像只是把生產(chǎn)環(huán)節(jié)重新創(chuàng)建了一個類煞茫,這確實(shí)比較像是一種編程習(xí)慣帕涌,此種解決方式被稱作簡單工廠模式
工廠函數(shù)、工廠類對具體的生成環(huán)節(jié)進(jìn)行了封裝续徽,這樣有利于代碼的后需擴(kuò)展蚓曼,即把功能劃分的更具體,4s店只負(fù)責(zé)銷售钦扭,汽車廠只負(fù)責(zé)制造