前言:
本篇相關內容分為3篇多態、繼承、封裝,這篇為第二篇 繼承。
Content:
- 繼承
1. 什么是繼承,繼承的作用和常用狀態?
2. python的多繼承、instance和type?
3. python中的super函數
4. python的MRO查找機制來對應多繼承和super
5. python的抽象基類
6. django等大大框架和python源碼中最常用的Mixin模式多繼承實例
?(下篇
- 封裝
1.數據封裝和私有屬性
2. 類變量和實例變量(對象變量)
3. 類屬性和實例屬性得查找順序(MRO)
4. 靜態方法 類方法和對象方法使用以及參數
5. python的接口和自省機制
6. 上下文管理器
?)
?
一 繼承
1.什么是繼承?繼承有哪些作用?常用?
? ? - 繼承的概念? :在OOP程序設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。
//INPUT:
class Animals(): def say(self): print ( " say something " ) def eat(self): print ( " eat something " ) class Duck(Animals): def say(self): print ( " gaga " ) class Dog(Animals): def say(self): print ( " wangwang " ) class Miao(Animals): def say(self): print ( " miaomiao " ) for i in [Duck(),Dog(),Miao()]: i.eat()
//OUTPUY:
eat something
eat something
eat something
? ? - 繼承的作用:可以解決代碼重用的問題。
? ? - 比較常用的應該就是在python相關web框架里這種比較比較復雜的框架,和許多第三方庫的類。天知道這些大牛的框架里面默默有效率的幫我們做了多少事- -。天知道我們直接用他們的方法有多方便。
?
2.python多繼承需要注意哪些?用instance和type來判斷python類繼承的關系。
a.多繼承需要注意的地方:
- 類繼承的順序和方法在多繼承類中查找的順序
? ? --.子類繼承父類時,在子類進行屬性調用的順序為:先查找自己的屬性字典,若自己的屬性字典中無該屬性,則會依次按照繼承父類的順序來依次查找父類的屬性字典;
? ? --.子類繼承父類,當父類和子類均有相同的屬性時,子類并不會影響父類的屬性。
總結起來就是: 按繼承的順序來依次查詢屬性,一旦查到則停止;子類和父類的屬性相互獨立,互不影響;子類可以調用父類的屬性,反之不行 ;
b.用instance和type來判斷兩個類的關系
instance和type都是用來判斷參數1是否是參數2類型,區別在于是否會考慮繼承關系。
# Python3.x 實例: class A: def add(self, x): y = x+1 print (y) class B(A): def add(self, x): super().add(x) b = B() b.add( 2) # 3
# Python2.x 實例: # !/usr/bin/python # -*- coding: UTF-8 -*- class A(object): # Python2.x 記得繼承 object def add(self, x): y = x+1 print (y) class B(A): def add(self, x): super(B, self).add(x) b = B() b.add( 2) # 3
?
C3線性是用于獲取多重繼承下繼承順序的一種算法。通常,被稱為 方法解析順序 ,即MRO(method resolution order)。
算法的名字“C3”并不是縮寫,而是指該算法的三大重要屬性:
- 前趨圖。作為有向無環圖,找不到任何的循環,通常用前趨圖來理解程序的依賴關系。
- 保持局部的優先次序。
- 單調性。
C3是1996年首次被提出。在python2.3及后續版本中,C3被選定為默認的解析算法。
一個類的C3線性表,是由兩部分進行merge操作得到的,第一部分是是它所有父類的C3線性表(parents' linearizations),第二部分是它所有父類組成的列表(parents list)。后者其實是局部的優先級列表。
在C3被應用之前,廣度優先和深度優先是被應用于解析順序的。
C3算法計算方法:有點類似拓撲排序(有向圖) ? ? ?想深刻了解 C3 還是自己查吧...下面有段簡單的多繼承代碼和其對應的拓撲排序的抽象圖 ( 所用代碼實例和圖片均來應用自別處,文章末尾有鏈接 )
class D(object): pass class E(object): pass class F(object): pass class C(D, F): pass class B(E, D): pass class A(B, C): pass if __name__ == ' __main__ ' : print A. __mro__
得到的輸出結果:
(< class ' __main__.A ' >, < class ' __main__.B ' >, < class ' __main__.E ' >, < class ' __main__.C ' >, < class ' __main__.D ' >, < class ' __main__.F ' >,' object ' >)
-
下面就是抽象出來的圖
我們就用拓撲排序來分析,但是這里會碰到同時存在好幾個點都是入度為0 (說人話,就是沒有被別人指向的點),這時按照樹的排序來,即 從左到右,從根到葉 ,這里 A 就是根。
所以具體情況就是:我們先找到了點 A只有它沒有被別人指向,輸出
A
;去掉A及其伸出的兩條線,剩 B 和 C 點同時滿足只指向別人,按照樹的順序從左到右,故先輸出
B
;去掉線剩 E 和 C ,輸出
E
;去線剩 C,輸出
C
;去線剩 D 和 F ,輸出
D
;去線只剩F ,輸出
F
;最后輸出
object
;得到的輸出結果:?
A B E C D F object
對比系統打印出的結果,順序是一致的。這樣下次你就可以裝逼地手動計算多繼承時調用類的順序了
以上結論來源鏈接:https://www.jianshu.com/p/6651ed35104c
class Bird: def __init__ (self): self.hungry = True def eat(self): if self.hungry: print ' Ahahahah ' else : print ' No thanks! ' class SongBird(Bird): def __init__ (self): self.sound = ' Squawk ' def sing(self): print self.song() sb = SongBird() sb.sing() # 能正常輸出 sb.eat() # 報錯,因為 songgird 中沒有 hungry 特性
這時候,如果需要繼承父類構造函數里的屬性,其實是可以有兩種方法的。
第一種 - 調用未綁定的超類構造方法(多用于舊版 python 陣營)
class SongBird(Bird): def __init__ (self): Bird. __init__ (self) self.sound = ' Squawk ' def sing(self): print self.song()
原理 :在調用了一個實例的方法時,該方法的self參數會自動綁定到實例上(稱為綁定方法);如果直接調用類的方法(比如Bird.__init__),那么就沒有實例會被綁定,可以自由提供需要的self參數(未綁定方法)。
第二種 - 使用super函數(只在新式類中有用)
class SongBird(Bird): def __init__ (self): super(SongBird,self). __init__ () self.sound = ' Squawk ' def sing(self): print self.song()
原理: 它會查找所有的超類,以及超類的超類,直到找到所需的特性為止。
from abc import ABC, abstractmethod class Talker(ABC): @abstractmethod def talk(self): pass
結果:
##抽象基類不能被實例化
>>> Talker() Traceback (most recent call last): File "" , line 1, in TypeError: Can ' t instantiate abstract class Talker with abstract methods talk
##沒有重寫方法的時候,繼承抽象基類的類 本質也是抽象類
class Knigget(Talker): pass ##由于沒有重寫方法talk,因此這個類也是抽象的,不能實例化。如果你試圖這樣做,將出現類似于前面的錯誤消息。
###繼承后重寫了方法就沒什么問題
class Knigget(Talker): def talk(self): print ( " Ni! " )
##output:
?
在設計類的繼承關系時,通常,主線都是單一繼承下來的,例如,
Ostrich
繼承自
Bird
。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓
Ostrich
除了繼承自
Bird
外,再同時繼承
Runnable
。這種設計通常稱之為Mixin。
為了更好地看出繼承關系,我們把
Runnable
和
Flyable
改為
RunnableMixin
和
FlyableMixin
。類似的,你還可以定義出肉食動物
CarnivorousMixin
和植食動物
HerbivoresMixin
,讓某個動物同時擁有好幾個Mixin:
class
Dog
(Mammal, RunnableMixin, CarnivorousMixin):
pass
Mixin的目的就是給一個類增加多個功能,這樣,在設計類的時候,我們優先考慮通過多重繼承來組合多個Mixin的功能,而不是設計多層次的復雜的繼承關系。
Python自帶的很多庫也使用了Mixin。舉個例子,Python自帶了
TCPServer
和
UDPServer
這兩類網絡服務,而要同時服務多個用戶就必須使用多進程或多線程模型,這兩種模型由
ForkingMixin
和
ThreadingMixin
提供。通過組合,我們就可以創造出合適的服務來。
比如,編寫一個多進程模式的TCP服務,定義如下:
class
MyTCPServer
(TCPServer, ForkingMixin):
pass
編寫一個多線程模式的UDP服務,定義如下:
class
MyUDPServer
(UDPServer, ThreadingMixin):
pass
如果你打算搞一個更先進的協程模型,可以編寫一個
CoroutineMixin
:
class
MyTCPServer
(TCPServer, CoroutineMixin):
pass
這樣一來,我們不需要復雜而龐大的繼承鏈,只要選擇組合不同的類的功能,就可以快速構造出所需的子類。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
