一文理清面向对象(封装、继承、多态)+ 实战案例
python是一門面向對象編程語言,對面向對象語言編碼的過程叫做面向對象編程。
面向對象是一種思想,與之相對的是面向過程。我們先簡單說一下面向過程。
面向過程其實就是把過程當做設計核心,根據問題的發展順序,依次解決問題,盡可能的把過程中涉及到的問題完善解決。他有他的優點,當拿到一個問題時,可以方便的按執行的步驟寫代碼,但是當邏輯關系變得復雜時,有一個地方出現差錯就會導致整個程序無從下手。
面向對象的編程語言還是很多的,例如C++、Java等。面向對象程序設計把計算機程序的執行看做一組對象的集合,每個對象之間進行消息的傳送處理。有一個顯著的優點就是,對某個對象進行修改,整個程序不會受到影響,自定義數據類型就是面向對象中的類的概念,而我們需要把他們的接口處理好就很好辦了。說了這么多話,有些小白已經看不下去了,那接下來我們進入主題。
上面說了,自定義數據類型就是面向對象中的類的概念。我們先介紹一下待會兒會用到的一些術語(我認為還是通過個例子更容易讓人理解):
# 首先我們定義一個類 class?A(object): # 這是一個類,class是創建一個類的標志# 類變量(類屬性):類屬性是指類的屬性,屬性就是我們剛學編程的時候聽過的變量。x = 7y = "asdf"def?__init__(self,name,age):self.name = nameself.age = age# 方法:方法就是在類外面我們寫的函數,放在類里就叫做一個方法def?func(self):c = 8????# 實例變量:定義在方法中的變量只作用于當前實例的類print("Hello World!")a = A() # 創建一個對象,實例化上面的代碼還需要再解釋一下:
object:注意類名后面括號里有個參數object,他代表所有類的基類,也叫作超類。
這就有了一個新式類和舊式類的概念:當用到多繼承的時候,如果子類中沒有想用的方法名或屬性名,他會自動回到上面去找。那么按廣度優先遍歷的方法去尋找就是新式類(object);深度優先(括號里啥也沒有)。
__init__():構造函數,實例化的時候若不顯示的定義,那么默認調用一個無參的,是初始化的意思。
封裝
含義:對外面隱藏對象的屬性和方法,僅提供接口。
作用:安全性(通過私有變量改變對外的使用),復用性。
# 以下程序關于學生成績,通過封裝函數,實現修改、顯示分數的功能 class?Student(object):def?__init__(self, name, score):# 屬性僅前面有兩個下劃線代表私有變量,外部無法訪問,因此我們定義了兩個新的方法,這樣可以避免外部通過score亂改分數,僅當我們自己知道接口才可以修改self.__name = nameself.__score = scoredef?info(self):print('name: %s ; score: %d'?% (self.__name,self.__score))def?getScore(self):return?self.__scoredef?setScore(self, score):self.__score = scorestu = Student('Tom',99) # 實例化 print('修改前分數:',stu.getScore()) stu.info() stu.setScore(59) # 重置分數 print('修改后分數:',stu.getScore()) stu.info()繼承
含義
前面我們提到過,面向對象編程有個好處就是代碼復用,而其中一種方法就是通過繼承機制。繼承就是說定義的一個新類,繼承現有的類,獲得現有類的非私有屬性、方法。提到個私有,就是上面提到的那個前面加兩個下劃線的那個東西,他在外部無法調用,繼承他的子類也不能。被繼承的那個類稱為基類、父類或超類,子類也可以叫做派生類。
特點
1、在繼承中,基類的構造方法(__init__()方法)不會被自動調用,需要在子類的構造方法中專門調用。
2、在調用基類的方法時需要加上基類的類名前綴,并帶上self參數變量。區別于在類中調用普通函數時不需要帶self參數。
3、在python中,首先查找對應類型的方法,如果在子類中找不到對應的方法,才到基類中逐個查找。
單繼承
直接上代碼,仔細理解一下里面的關系,我把講解都寫在注釋的地方。
(注:不同的軟件導入自定義庫的方式不太一樣,如果使用我的程序無法執行,可能是由于你的環境中不需要from 單繼承的實現.person import Person,而是直接from person import Person,后續代碼同理)
# 這是定義了一個基類 class?Person(object):def?__init__(self, name, age, money):self.name = nameself.age = ageself.__money = money # 私有屬性# 被引入時,繼承不了,但他們的set,get函數可以繼承def?setMoney(self,money):self.__money = moneydef?getMoney(self):return?self.__moneydef?run(self):print("run")def?eat(self):print("eat")下面是定義的一個子類,繼承自上方的類,來使用父類中的方法和屬性。
# 由于我將每個類寫在了不同的文件里,所以需要引入一下,這就和我們調用庫一樣 from 單繼承的實現.person import Personclass?Student(Person):def?__init__(self,name,age,stuid,money):# 調用父類中的__init__(),supper括號中的內容,在python3以后可以不寫,寫上更安全些super(Student,self).__init__(name,age,money) # 讓父類的self當做子類的對象# 子類可以由一些自己獨有的屬性或者方法self.stuid = stuid創建對象,通過子類使用父類的屬性和方法。
from?單繼承的實現.student import?Studentstu = Student('Tom',18,111,999) # 創建Student對象 # 下列方法和屬性均是在父類Person中定義的,在Student繼承之后,便可以直接使用 print(stu.name, stu.age) stu.run() print(stu.getMoney())顯示結果如下:
多繼承
上面的單繼承要多理解一下,單繼承理解了之后,多繼承也只是同時繼承了不止一個父類而已。下面直接給一個例子瞧瞧。
class?Father(object):def?__init__(self,money):self.money = moneydef?play(self):print("play")def?func(self):print("func1")class?Mother(object):def?__init__(self,facevalue):self.facevalue = facevaluedef?eat(self):print("eat")def?func(self):print("func2")class?Children(Father,Mother):def?__init__(self,money,facevalue):# 多繼承時調用父類的屬性Father.__init__(self,money)Mother.__init__(self,facevalue)def?main():c = Children(300,100)print(c.money,c.facevalue)c.play()c.eat()# 注意:如果多個父類中有相同的方法名,默認調用括號中前面的類c.func()if?__name__?== "__main__":main()多態
多態:是指一種事物的多種形態
多態性:多態性是指具有不同功能的函數可以使用相同的函數名,這樣就可以用一個函數名調用不同內容的函數。在面向對象方法中一般是這樣表述多態性:向不同的對象發送同一條消息,不同的對象在接收時會產生不同的行為(即方法)。也就是說,每個對象可以用自己的方式去響應共同的消息。所謂消息,就是調用函數,不同的行為就是指不同的實現,即執行不同的函數。
eg:在python中的“+”號,它既可以表示數字的加法,也可以表示字符串的拼接。
class?Animal(object):def?__init__(self, name):self.name = namedef?run(self):passdef?animalRun(self):self.run()class?Cat(Animal):def?run(self):print('cat is running')class?Dog(Animal):def?run(self):print('dog is running')d = Dog('dog') c = Cat('cat')Animal.animalRun(c) Animal.animalRun(d)看過上面多繼承和多態的例子你有沒有什么感覺,繼承是一個繼承多個,而多態是多個繼承一個。
小栗子
下面,小栗子來了,內容不要緊,關鍵是要理解面向對象的思想(python中,萬物皆對象)。這個例子來源于小時候經常聯機玩的cs,當時的cs還沒有現在這么豐富。扯遠了,繼續談對象。
首先,確定里面有哪些對象,當然只是示意,不會搞很復雜的內容。主要有好人、壞人、槍(槍的彈夾也可以寫一個類)、手榴彈,也就這些東西吧。接下來就要分別寫每個對象的內容了。
#?壞蛋/好人 class?Gengster(Person):# 初始化,血量默認為100def?__init__(self, gun, grenade, blood=100):self.gun = gunself.grenade = grenadeself.blood = blood# 人有開槍的功能def?fire(self,person):person.blood.amount -= 5?# 對誰開槍,那個人就要減血self.gun.shoot() # 這個人開槍,這又調用了槍的類,關于子彈的減少在槍的類里# 扔手榴彈,實際上是和槍一樣的def?fire2(self,person):person.blood -= 10self.grenade.damage() # 同樣通過另一個類來控制數量的減少,使代碼看起來簡潔點# 給彈夾里加子彈def?fillbullet(self):self.gun.bulletbox.bulletcount += 10# 補血,并保證滿血只能是100def?fillblood(self,num):self.blood += numif?self.blood > 100:self.blood = 100print("補血后血量:"?+ str(self.blood))# 槍class?Gun(object):# 初始化,把彈夾放里面,通過人來控制槍,槍再來控制彈夾def?__init__(self,bulletbox):self.bulletbox = bulletboxdef?shoot(self):if?self.bulletbox.bulletcount == 0:print('沒子彈了')else:self.bulletbox.bulletcount -= 1print(str(self) + '開一槍,還剩%d顆子彈'?% (self.bulletbox.bulletcount))# 彈夾class?Bulletbox(object):# 彈夾只需控制數量就好了def?__init__(self,bulletcount):self.bulletcount = bulletcount# 手榴彈,與槍類似class?Grenade(object):def?__init__(self,grenadecount):self.grenadecount = grenadecountdef?damage(self):if?self.grenadecount == 0:print('手雷沒有了')else:self.grenadecount -= 1print(str(self) + "轟他一炮,手雷還剩%d顆"?% (self.grenadecount))那么,現在人和武器都有了,就可以開始戰斗了。
現在這一套流程就結束了,剛開始看也許看不太懂,要仔細看一下每個類之間的關系,先想清楚了,再來看代碼是如何實現的。
有沒有看出來點區別,面向過程編程是就事論事,而面向對象,先把對象找出來,通過對象之間的關系把他們聯系起來。想想如果要用面向過程來實現這個,代碼會寫成什么樣子呢。
然而并沒有結束,前面好人和壞人的程序基本上就差不多的,如果考慮含有不同的話,這時候就用到了上面講到的繼承,繼承的一個特點就是復用。我們可以用繼承來寫一下,如果你說這個也沒少幾行代碼嘛,如果在實際當中你要創建成百上千的對象呢,難道還要每個都復制粘貼改代碼嗎,還占空間對不對。
首先,好人壞人都是人對吧,那么就可以先創建一個人的類,然后分別寫好人壞人,繼承一下人的類就好了。
class?Person(object):def?__init__(self, gun, grenade, blood):self.gun = gunself.grenade = grenadeself.blood = blooddef?fire(self, person):person.blood -= 5self.gun.shoot()print(str(person) + "血量減少5,剩余"?+ str(person.blood) )def?fire2(self, person):person.blood -= 10self.grenade.damage()print(str(person) + "血量減少10,剩余"?+ str(person.blood) )def?fillbullet(self):self.gun.bulletbox.bulletcount += 10def?fillblood(self,num):self.blood += numif?self.blood > 100:self.blood = 100print(str(self) + "補血后血量:"?+ str(self.blood))from cs.person import Personclass?Profector(Person):def?__init__(self, gun, grenade, blood = 100):super(Profector,self).__init__(gun, grenade, blood)class?Gengster(Person):def?__init__(self, gun, grenade, blood=100):super(Gengster, self).__init__(gun, grenade, blood)看到這里,也許有人已經懵了,不要著急,慢慢理解其中的關系。其實仔細看結果發現還有問題,血量減少的人的對象還是正常的,然而看一下開槍的人。有沒有發現好人1和好人2的對象時同一個地址呢,他們的子彈也是累積的遞減;壞人使用手榴彈也是。為什么開槍的人會是這樣,而受傷的人卻是正常的呢?提醒一下,我們前面創建的那些對象,有些是為了下一個對象調用而準備的,看看是在哪里出錯的。
END
來和小伙伴們一起向上生長呀~~~
掃描下方二維碼,添加小詹微信,可領取千元大禮包并申請加入 Python學習交流群,群內僅供學術交流,日常互動,如果是想發推文、廣告、砍價小程序的敬請繞道!一定記得備注「交流學習」,我會盡快通過好友申請哦!
(添加人數較多,請耐心等待)
總結
以上是生活随笔為你收集整理的一文理清面向对象(封装、继承、多态)+ 实战案例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么我们程序员不把软件开发当回事?
- 下一篇: 微信重大更新!这特么是为上班摸鱼开发的吧