Python面向?qū)ο笕筇卣鳎豪^承、封裝和多態(tài)的深度解析
在面向?qū)ο蟪绦蛟O(shè)計中,對象可以看做是數(shù)據(jù)(特性)以及由一系列可以存取、操作這些數(shù)據(jù)的方法所組成的集合。Python是面向?qū)ο蟮恼Z言,支持面向?qū)ο缶幊痰娜筇匦裕豪^承、封裝(隱藏)、多態(tài),本文將逐一講解Python的三大特性。
封裝
繼承的基本概念以及使用
封裝的概念:隱藏內(nèi)部實現(xiàn)的細節(jié),只對外提供操作方法(接口)。這個概念我們在上一小節(jié)中也已經(jīng)學(xué)習過了,我們主要是去了解其在代碼中是如何去實現(xiàn)的:通過有著類似 訪問修飾限定符 功能的下劃線來實現(xiàn)。
權(quán)限控制:通過對屬性或方法添加單下劃線、雙下劃線以及首尾雙下劃線來實現(xiàn)。
下劃線種類 | 功能 | |
單下劃線開頭 | 表示protected,受保護的成員,這類成員被視為僅供內(nèi)部家族使用,允許類本身和子類進行訪問,但實際上它可以被外部代碼訪問 | |
雙下劃線開頭 | 表示private,私有的成員,這類成員只允許定義該屬性或方法的類本身進行訪問 | |
首尾雙下劃線 | 一般表示特殊的方法 |
代碼演示:
class Dog(): # 首尾雙線劃線 ——> 特殊方法 def __init__(self, name, age): # 雙下劃線開頭 ——> private修飾,只能在類內(nèi)訪問 self.__name = name # 單下劃線開頭 ——> protected修飾,在類內(nèi)與子類才能訪問 self._age = age # 單下劃線開頭 ——> protected修飾,在類內(nèi)與子類才能訪問 def _fun1(self): print('這是被protected所修飾的方法') # 雙下劃線開頭 ——> private修飾,在類內(nèi)才能訪問 def __fun2(self): print('這是被private所修飾的方法') # 這里是類外了 dog = Dog('小劉', 18) print(dog._age) # print(dog.__name) dog._fun1() # dog.__fun2() # 和上面一樣,直接去訪問的話,就會報錯, # 但是我們可以使用 對象.__dir()__ 或者 dir(對象) 先去查看所有的屬性與方法 # 然后通過其中的"屬性"與"方法名"去調(diào)用真正的屬性與方法 # print(dog.__dir__()) # print(dir(dog)) print(dog._Dog__name) dog._Dog__fun2()
運行結(jié)果:
根據(jù)上面的訪問方式,我們可以推測出:被 protected、private 所修飾 方法 與 屬性只是在類中對應(yīng)的名稱發(fā)生了變化,而我們不知道,但是可以通過特殊手段知曉,從而繼續(xù)訪問。
但是上面的方式不是很推薦,類似與 Java中的反射機制了,有點反常規(guī)。除了上面這種方式,Python還提供了兩種方式來實現(xiàn)訪問 與 修改 私有是屬性與方法。
1、在 私有的方法 或者 屬性上,進行套殼處理。
2、在1的基礎(chǔ)上,通過 @property 裝飾器 來修飾方法,使其變?yōu)閷傩?,就變?yōu)樵L問與修改屬性了,最終也會變的很簡單。
代碼演示:
class Dog(): def __init__(self, name, age): self.__name = name self.age = age # 如果想要去訪問除了,使用dir()之外,還有兩種方式: # 使用實例方法去間接訪問與修改 def get_name(self): return self.__name def set_name(self, name): self.__name = name # 使用@property裝飾器,將方法轉(zhuǎn)為屬性使用 @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name dog = Dog('老劉', 8) # 1、通過實例方法的形式去使用 print(dog.get_name()) dog.set_name('小劉') print('='*18) # 2、通過@property裝飾器,將方法轉(zhuǎn)為屬性 print(dog.name) dog.name = '老劉' # 修改屬性的樣式,去傳參 print(dog._Dog__name) print('='*18) dog._Dog__name = '小劉' print(dog._Dog__name)
運行結(jié)果:
注意:我們在使用 @property 裝飾器,將屬性轉(zhuǎn)為方法時,取值的方法,必須被 @property 修飾,而修改值的方法,必須被 @取值方法名. setter修飾。
繼承
繼承的基本概念以及使用
繼承是指一個類(子類、派生類)繼承另一個類(父類、基類)的屬性與方法。
在Python中一個子類可以繼承N多個父類,這是與 Java決然不同的一點。Java屬于單繼承,而Python屬于多繼承,并且每個類都默認繼承自object類。當然,一個子類可以有多個父類,一個父類也可以有多個子類。
語法:
class 類名([父類列表]):
注意:當一個類的父類只有object類時,默認是可以不寫父類的,但是如果一個類有除object類之外的其他類,就需要將這些類全部寫到 () 中,當然這個()在只有繼承object類的情況下,也是可以不寫的,但是建議還是要寫上去。
代碼演示:
# 默認繼承object類,但是這個可以不寫 class Animal(object): def __init__(self, name, age, gender,sort): self.name = name self.age = age self.gender = gender self.__sort = sort class Dog(Animal): pass # 子類繼承父類,會將父類中除private修飾的方法與屬性全部拿過來 # 繼承了 __init__方法,以及 name、age、gender屬性 dog = Dog('小黑', 3, '雄', '哈士奇') print(dog.name) print(dog.age) print(dog.gender) # 同樣需要特殊手段才能訪問到 print(dog._Animal__sort)
運行結(jié)果:
注意:我們在實例化一個對象時,會先去調(diào)用構(gòu)造方法(init 方法)給對象的屬性進行初始化賦值,如果一個類沒有構(gòu)造方法,但是由于這個類是默認繼承自object類,因此會調(diào)用object類中默認的 init 方法,但如果這個類繼承了其他類,就會先在其他類中去搜索這個方法,如果有則調(diào)用;反之,則還是去調(diào)用object類的。 而在上面的代碼中,Animal 類是有 init 方法,因此會去調(diào)用Animal 類的。
我們再看看多繼承的代碼:
class Person(): def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def show(self): print(f'我叫{self.name},今年{self.age},性別是{self.gender}') class Father(Person): def __init__(self,name,age,gender): # 可以直接調(diào)用父類的初始化方法 super().__init__(name,age,gender) # 下面這種方式是錯誤的 # super().name = name # super().age = age # super().gender = gender # 下面的方式也行,但是不推薦 # self.name = name # self.age = age # self.gender = gender class Mother(Person): def __init__(self, name, age, gender): # 可以直接調(diào)用父類的初始化方法 super().__init__(name, age, gender) # 下面這種方式是錯誤的 # super().name = name # super().age = age # super().gender = gender # 下面的方式也行,但是不推薦 # self.name = name # self.age = age # self.gender = gender class Son(Father, Mother): def __init__(self, name, age, gender): # 可以直接調(diào)用父類的初始化方法 super().__init__(name, age, gender) # 下面這種方式是錯誤的 # super().name = name # super().age = age # super().gender = gender # 下面的方式也行,但是不推薦 # self.name = name # self.age = age # self.gender = gender father = Father('張三',25,'男') mother = Mother('紅粉佳人',28,'女') son = Son('趙四',29,'男') father.show() mother.show() son.show()
運行結(jié)果:
上面的代碼中,Son類、Mother類、Father類都是繼承自Person類的屬性與方法,它們是公用Person類的屬性與方法。
注意:上述 Son類繼承了多個父類,當我們?nèi)ナ褂?super().init 方法初始化子類對象時,默認是按照父類在Son()中的順序來查找的,這里是Father類定義聲明在前,因此這里調(diào)用的就是 Father 類的 init 方法,如果想要指定某個類的 init 方法的話,就需要使用 類名.init() 方法。例如:
# 這里的self代指當前Son類的實例對象 Mother.__init__(self,name,age,gender) # 可替換Son類中是super.init
方法重寫
當子類繼承父類時,子類就擁有了父類中的 公有成員、方法和受保護的成員、方法。如果我們對父類中有些成員不滿意的話,子類就可以重新定義成員。同理,方法也是如此,但是重新定義方法麻煩了,那有什么辦法呢?方法重寫,即 偷梁換柱,表面上這個方法還是原來的方法,但是其內(nèi)部實現(xiàn)的結(jié)構(gòu)早就發(fā)生變化了。
重寫的要求:方法名必須一樣,至于方法的返回值類型,參數(shù)列表、訪問權(quán)限這些可以不一樣。
代碼演示:
class Animal(): def __init__(self,name,age): self.name = name self.age = age def eat(self): print(f'{self.name}正在吃狗糧,看小黑玩飛盤') class Dog(Animal): def __init__(self,name,age): super().__init__(name,age) # 由于Animal類的eat方法只是隨便說在吃啥,并不具體, # 但我們想要打印出具體的食物,因此可以重寫(外殼不變,核心變化) def eat(self): print(f'{self.name}正在玩飛盤~') class Cat(Animal): def __init__(self,name,age): super().__init__(name,age) dog = Dog('小黑', 2) # 子類重寫父類的方法之后,父類再去調(diào)用eat方法,就不再是調(diào)用父類的,而是自己的 dog.eat() cat = Cat('小黃', 3) # 子類沒有重寫父類的eat方法,因此還是去調(diào)用父類的eat方法 cat.eat()
運行結(jié)果:
其實,重寫就是在子類中定義了一個與父類重名的方法,只不過因為 Python中語法的檢查不是很嚴格,因此這里就很容易不理解。
在Python中,我們就將重寫看作是子類定義了一個與父類重名的方法即可,在調(diào)用時,如果子類沒有這個方法,那么就是調(diào)用父類的同名方法,如果還沒有就會報錯。但如果子類有這個方法,那么就是直接調(diào)用子類的方法,即使父類有,也不去調(diào)用("地頭蛇原則")。
多態(tài)
多態(tài)的概念以及基本使用
多態(tài)是指多種形態(tài),當去完成某個行為時,不同的對象可能會產(chǎn)生不同的形態(tài)。
例如,我們在類和對象中說的小故事:當劉建明 與 陳永仁 遇到 韓琛 時,兩人打招呼的方式不一樣,這就是不同對象 在 完成同一個行為時,兩者所產(chǎn)生的形態(tài)不同。
再比如,我們每天都會吃早餐,但是不同的人 在 面對這件事情時,所表現(xiàn)出的行為就不一樣,A 可能吃面條,B可能吃包子、餃子,C可能吃大米飯等。
Python中的多態(tài)與Java、C++不同,Python中的多態(tài)只需要滿足有該方法,然后讓不同的對象去調(diào)用即可,而 Java 中的多態(tài)需要滿足三個條件:向上轉(zhuǎn)型(父類引用指向子類對象)、子類重寫父類的方法、向上轉(zhuǎn)型的父類調(diào)用該方法(被重寫的方法),這時就不再是調(diào)用父類的方法,而是調(diào)用子類的方法。但 Python中,只要這兩個類有相同的方法,當兩者賦值給同一個對象時,去調(diào)用同一個方法就會表現(xiàn)出不同的行為。
其實,多態(tài)就是表層對象一樣,內(nèi)部實際的對象不一樣,那么在調(diào)用同一個方法時,雖然看似是這個表層的對象所調(diào)用的,但實際是內(nèi)部的對象所調(diào)用并執(zhí)行的。
代碼演示:
class Animal(): def eat(self): print('在玩飛盤') class Person(): def eat(self): print('到處溜達') class Dog(): def eat(self): print('瘋狂奔跑') class Cat(): def eat(self): print('偷吃罐頭') # 定義一個函數(shù),傳入obj對象,并調(diào)用該對象的eat方法 def eat(obj): obj.eat() # 方法得通過類或者對象去"."調(diào)用,而函數(shù)是直接傳參調(diào)用,不要弄混了 eat(Animal()) # 這里需要傳入對象,即:類名() eat(Person()) eat(Dog()) eat(Cat())
運行結(jié)果:
總結(jié)
到此這篇關(guān)于Python面向?qū)ο笕筇卣鳎豪^承、封裝(隱藏)、多態(tài)的文章就介紹到這了,更多相關(guān)Python面向?qū)ο笕筇卣鲀?nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python Matplotlib繪制箱型圖(箱線圖)boxplot的方法詳解
箱線圖(箱型圖)主要作用是發(fā)現(xiàn)數(shù)據(jù)內(nèi)部整體的分布分散情況,包括上下限、各分位數(shù)、異常值等,本文為大家整理了Matplotlib繪制箱型圖的所以方法,希望對大家有所幫助2023-05-05基于Python實現(xiàn)一個簡單的學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細介紹了如何利用python實現(xiàn)簡單的學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-12-12Python面向?qū)ο蟪绦蛟O(shè)計中類的定義、實例化、封裝及私有變量/方法詳解
這篇文章主要介紹了Python面向?qū)ο蟪绦蛟O(shè)計中類的定義、實例化、封裝及私有變量/方法,結(jié)合具體實例形式較為詳細的分析了Python面向?qū)ο蟪绦蛟O(shè)計中類的定義、實例化、封裝、私有變量、私有方法等相關(guān)使用技巧,需要的朋友可以參考下2019-02-02Python使用PyMongo4.x操作MongoDB的教程分享
PyMongo是一個Python編程語言中用于連接和操作MongoDB數(shù)據(jù)庫的庫,它提供了豐富的功能和API,使開發(fā)者能夠在Python中輕松地進行MongoDB的數(shù)據(jù)交互和管理,本文給大家總結(jié)了Python如何使用PyMongo4.x操作MongoDB,需要的朋友可以參考下2023-09-09python BeautifulSoup設(shè)置頁面編碼的方法
這篇文章主要介紹了python BeautifulSoup設(shè)置頁頁編碼的方法,本文直接給出代碼救命,需要的朋友可以參考下2015-04-04Python使用tkinter實現(xiàn)搖骰子小游戲功能的代碼
這篇文章主要介紹了Python使用tkinter實現(xiàn)的搖骰子小游戲功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07