面向对象之三大特性:继承,封装,多态
python面向對象的三大特性:繼承,封裝,多態。
1. 封裝:?把很多數據封裝到?個對象中. 把固定功能的代碼封裝到?個代碼塊, 函數, 對象, 打包成模塊. 這都屬于封裝的思想. 具體的情況具體分析. 比如. 你寫了?個很?B的函數. 那這個也可以被稱為封裝. 在?向對象思想中. 是把?些看似?關緊要的內容組合到?起統?進?存儲和使?. 這就是封裝.?
2. 繼承:??類可以?動擁有?類中除了私有屬性外的其他所有內容. 說?了, ??可以隨便?爹的東?. 但是朋友們, ?定要認清楚?個事情. 必須先有爹, 后有??. 順序不能亂, 在python中實現繼承非常簡單. 在聲明類的時候, 在類名后?添加?個?括號,就可以完成繼承關系. 那么什么情況可以使?繼承呢? 單純的從代碼層?上來看. 兩個類具有相同的功能或者特征的時候. 可以采?繼承的形式. 提取?個?類, 這個?類中編寫著兩個類相同的部分. 然后兩個類分別取繼承這個類就可以了. 這樣寫的好處是我們可以避免寫很多重復的功能和代碼. 如果從語義中去分析的話. 會簡單很多. 如果語境中出現了x是?種y. 這時, y是?種泛化的概念. x比y更加具體. 那這時x就是y的?類. 比如. 貓是?種動物. 貓繼承動物. 動物能動. 貓也能動. 這時貓在創建的時候就有了動物的"動"這個屬性. 再比如, ?骨精是?個妖怪. 妖怪天?就有?個比較不好的功能叫"吃?", ?骨精?出?就知道如何"吃?". 此時 ?骨精繼承妖精.
3. 多態:?同?個對象, 多種形態. 這個在python中其實是很不容易說明?的. 因為我們?直在?. 只是沒有具體的說. 比如. 我們創建?個變量a = 10 , 我們知道此時a是整數類型. 但是我們可以通過程序讓a = "alex", 這時, a?變成了字符串類型. 這是我們都知道的. 但是, 我要告訴你的是. 這個就是多態性. 同?個變量a可以是多種形態。
一? 封裝? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
封裝,顧名思義就是將內容封裝到某個地方,以后再去調用被封裝在某處的內容。
所以,在使用面向對象的封裝特性時,需要:
- 將內容封裝到某處
- 從某處調用被封裝的內容
第一步:將內容封裝到某處
?self 是一個形式參數,當執行 obj1 = Foo('wupeiqi', 18 ) 時,self 等于 obj1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??當執行 obj2 = Foo('alex', 78 ) 時,self 等于 obj2
所以,內容其實被封裝到了對象 obj1 和 obj2 中,每個對象中都有 name 和 age 屬性,在內存里類似于下圖來保存。
第二步:從某處調用被封裝的內容
調用被封裝的內容時,有兩種情況:
- 通過對象直接調用
- 通過self間接調用
1、通過對象直接調用被封裝的內容
上圖展示了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性
class Foo:def __init__(self, name, age):self.name = nameself.age = ageobj1 = Foo('wupeiqi', 18) print obj1.name # 直接調用obj1對象的name屬性 print obj1.age # 直接調用obj1對象的age屬性 obj2 = Foo('alex', 73) print obj2.name # 直接調用obj2對象的name屬性 print obj2.age # 直接調用obj2對象的age屬性2、通過self間接調用被封裝的內容
執行類中的方法時,需要通過self間接調用被封裝的內容
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef detail(self):print self.nameprint self.ageobj1 = Foo('wupeiqi', 18) obj1.detail() # Python默認會將obj1傳給self參數,即:obj1.detail(obj1),所以,此時方法內部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18 obj2 = Foo('alex', 73) obj2.detail() # Python默認會將obj2傳給self參數,即:obj1.detail(obj2),所以,此時方法內部的 self = obj2,即:self.name 是 alex ; self.age 是 78綜上所述,對于面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然后通過對象直接或者self間接獲取被封裝的內容。
二 多態? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
多態,同一個對象,多種形態。python默認支持多態。
# 在java或者c#定義變量或者給函數傳值必須定義數據類型,否則就報錯。def func(int a):print('a必須是數字')# 而類似于python這種弱定義類語言,a可以是任意形態(str,int,object等等)。 def func(a):print('a是什么都可以')# 再比如: class F1:passclass S1(F1):def show(self):print 'S1.show'class S2(F1):def show(self):print 'S2.show'# 由于在Java或C#中定義函數參數時,必須指定參數的類型 # 為了讓Func函數既可以執行S1對象的show方法,又可以執行S2對象的show方法,所以,定義了一個S1和S2類的父類 # 而實際傳入的參數是:S1對象和S2對象def Func(F1 obj): """Func函數需要接收一個F1類型或者F1子類的類型"""print obj.show()s1_obj = S1() Func(s1_obj) # 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show Python偽代碼實現Java或C # 的多態 多態舉例 python中有一句諺語說的好,你看起來像鴨子,那么你就是鴨子。 對于代碼上的解釋其實很簡答: class A:def f1(self):print('in A f1')def f2(self):print('in A f2')class B:def f1(self):print('in A f1')def f2(self):print('in A f2')obj = A() obj.f1() obj.f2()obj2 = B() obj2.f1() obj2.f2() # A 和 B兩個類完全沒有耦合性,但是在某種意義上他們卻統一了一個標準。 # 對相同的功能設定了相同的名字,這樣方便開發,這兩個方法就可以互成為鴨子類型。# 這樣的例子比比皆是:str tuple list 都有 index方法,這就是統一了規范。 # str bytes 等等 這就是互稱為鴨子類型。 鴨子類型三 類的約束? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?先, 你要清楚. 約束是對類的約束.?
用一個例子說話:
公司讓小明給他們的網站完善一個支付功能,小明寫了兩個類,如下:
class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)a = Alipay() a.pay(100)b = QQpay() b.pay(200)但是上面這樣寫不太放方便,也不合理,老大說讓他整改,統一一下付款的方式,小明開始加班整理:
class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)def pay(obj,money): # 這個函數就是統一支付規則,這個叫做: 歸一化設計。 obj.pay(money)a = Alipay() b = QQpay()pay(a,100) pay(b,200)寫了半年的接口,小明終于接了大項目了,結果公司沒品位,招了一個野生的程序員春哥接替小明的工作,老大給春哥安排了任務,讓他寫一個微信支付的功能:
class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay: # 野生程序員一般不會看別人怎么寫,自己才是最好,結果......def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay() b = QQpay()pay(a,100) pay(b,200)c = Wechatpay() c.fuqian(300)結果春哥,受懲罰了,限期整改,那么春哥,發奮圖強,看了太白教你學python的相關資料,重新梳理的代碼:
class Payment: """ 此類什么都不做,就是制定一個標準,誰繼承我,必須定義我里面的方法。"""def pay(self,money):passclass QQpay(Payment):def pay(self,money):print('使用qq支付%s元' % money)class Alipay(Payment):def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay(Payment):def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay() b = QQpay()pay(a,100) pay(b,200)c = Wechatpay() c.fuqian(300)但是,這樣還會有問題,如果再來野生程序員,他不看其他的支付方式,也不知道為什么繼承的類中要定義一個沒有意義的方法,所以他會是會我行我素:
class Payment: """ 此類什么都不做,就是制定一個標準,誰繼承我,必須定義我里面的方法。"""def pay(self,money):passclass QQpay(Payment):def pay(self,money):print('使用qq支付%s元' % money)class Alipay(Payment):def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay(Payment):def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay() b = QQpay()pay(a,100) pay(b,200)c = Wechatpay() c.fuqian(300)所以此時我們要用到對類的約束,對類的約束有兩種:
1. 提取?類. 然后在?類中定義好?法. 在這個?法中什么都不??. 就拋?個異常就可以了. 這樣所有的?類都必須重寫這個?法. 否則. 訪問的時候就會報錯.?
2. 使?元類來描述?類. 在元類中給出?個抽象?法. 這樣?類就不得不給出抽象?法的具體實現. 也可以起到約束的效果.
先用第一種方式解決:
class Payment:"""此類什么都不做,就是制定一個標準,誰繼承我,必須定義我里面的方法。"""def pay(self,money):raise Exception("你沒有實現pay方法")class QQpay(Payment):def pay(self,money):print('使用qq支付%s元' % money)class Alipay(Payment):def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay(Payment):def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay() b = QQpay() c = Wechatpay() pay(a,100) pay(b,200) pay(c,300)第二種方式:引入抽象類的概念處理。
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): # 抽象類 接口類 規范和約束 metaclass指定的是一個元類 @abstractmethoddef pay(self):pass # 抽象方法class Alipay(Payment):def pay(self,money):print('使用支付寶支付了%s元'%money)class QQpay(Payment):def pay(self,money):print('使用qq支付了%s元'%money)class Wechatpay(Payment):# def pay(self,money):# print('使用微信支付了%s元'%money)def recharge(self):passdef pay(a,money):a.pay(money)a = Alipay() a.pay(100) pay(a,100) # 歸一化設計:不管是哪一個類的對象,都調用同一個函數去完成相似的功能 q = QQpay() q.pay(100) pay(q,100) w = Wechatpay() pay(w,100) # 到用的時候才會報錯# 抽象類和接口類做的事情 :建立規范 # 制定一個類的metaclass是ABCMeta, # 那么這個類就變成了一個抽象類(接口類) # 這個類的主要功能就是建立一個規范總結: 約束. 其實就是?類對?類進?約束. ?類必須要寫xxx?法. 在python中約束的?式和?法有兩種:
1. 使?抽象類和抽象?法, 由于該?案來源是java和c#. 所以使?頻率還是很少的
2. 使??為拋出異常的?案. 并且盡量拋出的是NotImplementError. 這樣比較專業, ?且錯誤比較明確.(推薦)
四. super()深入了解? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
super是嚴格按照類的繼承順序執行!!!?
class A:def f1(self):print('in A f1')def f2(self):print('in A f2')class Foo(A):def f1(self):super().f2()print('in A Foo')obj = Foo() obj.f1() super可以下一個類的其他方法 class A:def f1(self):print('in A')class Foo(A):def f1(self):super().f1()print('in Foo')class Bar(A):def f1(self):print('in Bar')class Info(Foo,Bar):def f1(self):super().f1()print('in Info f1')obj = Info() obj.f1()''' in Bar in Foo in Info f1 ''' print(Info.mro()) # [<class '__main__.Info'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <class '__main__.A'>, <class 'object'>] super()嚴格按照類的mro順序執行 class A:def f1(self):print('in A')class Foo(A):def f1(self):super().f1()print('in Foo')class Bar(A):def f1(self):print('in Bar')class Info(Foo,Bar):def f1(self):super(Foo,self).f1()print('in Info f1')obj = Info() obj.f1() 再來抽象類? ? ? ?
什么是抽象類
? ? 與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在于只能被繼承,不能被實例化
為什么要有抽象類
??? 如果說類是從一堆對象中抽取相同的內容而來的,那么抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要么是吃一個具體的香蕉,要么是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。
? ? 從設計角度去看,如果類是從現實對象抽象而來的,那么抽象類就是基于類抽象而來的。
從實現角度來看,抽象類與普通類的不同之處在于:抽象類中有抽象方法,該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的,即將揭曉答案
在python中實現抽象類
#一切皆文件 import abc #利用abc模塊實現抽象類class All_file(metaclass=abc.ABCMeta):all_type='file'@abc.abstractmethod #定義抽象方法,無需實現功能def read(self):'子類必須定義讀功能'pass@abc.abstractmethod #定義抽象方法,無需實現功能def write(self):'子類必須定義寫功能'pass# class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('文本數據的讀取方法')def write(self):print('文本數據的讀取方法')class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('硬盤數據的讀取方法')def write(self):print('硬盤數據的讀取方法')class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法def read(self):print('進程數據的讀取方法')def write(self):print('進程數據的讀取方法')wenbenwenjian=Txt()yingpanwenjian=Sata()jinchengwenjian=Process()#這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read()print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type) View Code抽象類與接口類
抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。
抽象類是一個介于類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計?
在python中,并沒有接口類這種東西,即便不通過專門的模塊定義接口,我們也應該有一些基本的概念。
1.多繼承問題
在繼承抽象類的過程中,我們應該盡量避免多繼承;
而在繼承接口的時候,我們反而鼓勵你來多繼承接口
2.方法的實現
在抽象類中,我們可以對一些抽象方法做出基礎實現;
而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現
抽象類與接口類
1.多繼承問題 在繼承抽象類的過程中,我們應該盡量避免多繼承; 而在繼承接口的時候,我們反而鼓勵你來多繼承接口2.方法的實現 在抽象類中,我們可以對一些抽象方法做出基礎實現; 而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現轉載于:https://www.cnblogs.com/ls13691357174/p/10476773.html
總結
以上是生活随笔為你收集整理的面向对象之三大特性:继承,封装,多态的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单快速搭建钓鱼wifi
- 下一篇: Sobel硬件实现的硬件代码分析(三)