python面向對象的三大特性:繼承,封裝,多態。java
1. 封裝: 把不少數據封裝到⼀個對象中. 把固定功能的代碼封裝到⼀個代碼塊, 函數, 對象, 打包成模塊. 這都屬於封裝的思想. 具體的狀況具體分析. 好比. 你寫了⼀個很⽜B的函數. 那這個也能夠被稱爲封裝. 在⾯向對象思想中. 是把⼀些看似⽆關緊要的內容組合到⼀起統⼀進⾏存儲和使⽤. 這就是封裝.python
2. 繼承: ⼦類能夠⾃動擁有⽗類中除了私有屬性外的其餘全部內容. 說⽩了, ⼉⼦能夠隨便⽤爹的東⻄. 可是朋友們, ⼀定要認清楚⼀個事情. 必須先有爹, 後有⼉⼦. 順序不能亂, 在python中實現繼承很是簡單. 在聲明類的時候, 在類名後⾯添加⼀個⼩括號,就能夠完成繼承關係. 那麼什麼狀況可使⽤繼承呢? 單純的從代碼層⾯上來看. 兩個類具備相同的功能或者特徵的時候. 能夠採⽤繼承的形式. 提取⼀個⽗類, 這個⽗類中編寫着兩個類相同的部分. 而後兩個類分別取繼承這個類就能夠了. 這樣寫的好處是咱們能夠避免寫不少重複的功能和代碼. 若是從語義中去分析的話. 會簡單不少. 若是語境中出現了x是⼀種y. 這時, y是⼀種泛化的概念. x比y更加具體. 那這時x就是y的⼦類. 好比. 貓是⼀種動物. 貓繼承動物. 動物能動. 貓也能動. 這時貓在建立的時候就有了動物的"動"這個屬性. 再好比, ⽩骨精是⼀個妖怪. 妖怪天⽣就有⼀個比較很差的功能叫"吃⼈", ⽩骨精⼀出⽣就知道如何"吃⼈". 此時 ⽩骨精繼承妖精.程序員
3. 多態: 同⼀個對象, 多種形態. 這個在python中實際上是很不容易說明⽩的. 由於咱們⼀直在⽤. 只是沒有具體的說. 好比. 咱們建立⼀個變量a = 10 , 咱們知道此時a是整數類型. 可是咱們能夠經過程序讓a = "alex", 這時, a⼜變成了字符串類型. 這是咱們都知道的. 可是, 我要告訴你的是. 這個就是多態性. 同⼀個變量a能夠是多種形態。c#
封裝,顧名思義就是將內容封裝到某個地方,之後再去調用被封裝在某處的內容。微信
因此,在使用面向對象的封裝特性時,須要:函數
第一步:將內容封裝到某處微信支付
self 是一個形式參數,當執行 obj1 = Foo('wupeiqi', 18 ) 時,self 等於 obj1網站
當執行 obj2 = Foo('alex', 78 ) 時,self 等於 obj2設計
因此,內容其實被封裝到了對象 obj1 和 obj2 中,每一個對象中都有 name 和 age 屬性,在內存裏相似於下圖來保存。code
第二步:從某處調用被封裝的內容
調用被封裝的內容時,有兩種狀況:
一、經過對象直接調用被封裝的內容
上圖展現了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式能夠如此調用被封裝的內容:對象.屬性名
class Foo: def __init__(self, name, age): self.name = name self.age = age obj1 = 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屬性
二、經過self間接調用被封裝的內容
執行類中的方法時,須要經過self間接調用被封裝的內容
class Foo: def __init__(self, name, age): self.name = name self.age = age def detail(self): print self.name print self.age obj1 = 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: pass class 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):pass 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() pay(a,100) pay(b,200) c = Wechatpay() c.fuqian(300)
可是,這樣還會有問題,若是再來野生程序員,他不看其餘的支付方式,也不知道爲何繼承的類中要定義一個沒有意義的方法,因此他會是會我行我素:
class Payment: """ 此類什麼都不作,就是制定一個標準,誰繼承我,必須定義我裏面的方法。 """ def pay(self,money):pass 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() 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指定的是一個元類 @abstractmethod def 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):pass def 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是嚴格按照類的繼承順序執行!!!
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()