1.繼承與派生 上文我們已經說過,Python中一切皆對象。我們從對象中抽取了共同特征和技能,得到了類的概念。類與類之間也有共同特征,我們可以從有共同特征和技能的類中提取共同的技能和特征,叫做父類。 比如老師和學生,都有名字,年紀,生日,性別等等,都會走,說話,吃飯。。。我們就可以從老師和學生中總結 ...
1.繼承與派生
上文我們已經說過,Python中一切皆對象。我們從對象中抽取了共同特征和技能,得到了類的概念。類與類之間也有共同特征,我們可以從有共同特征和技能的類中提取共同的技能和特征,叫做父類。
比如老師和學生,都有名字,年紀,生日,性別等等,都會走,說話,吃飯。。。我們就可以從老師和學生中總結出來一個‘人’類,稱為父類,那老師和學生就是‘人’類的子類,子類繼承父類,就有了父類的特征和方法。
繼承是一種什麼‘是’什麼的關係,繼承是一種產生新類的方法,當然目的也是為了減少代碼重用。
繼承的 基本形式是:
class People: pass class Student(People):#People稱為基類或者父類 pass
在Python中支持多繼承,一個子類可以繼承多個父類
我們可以通過__bases__的方法查看繼承的所有父類,會返回一個元組。
class People: pass class Animals: pass class Student(People,Animals): pass print(Student.__bases__)#(<class '__main__.People'>, <class '__main__.Animals'>) print(People.__bases__)#(<class 'object'>,)
可以看到,在People父類中,預設也繼承了一個object類,這就是新式類和經典類的區別:
凡是繼承了object類的類及其子類,都稱為新式類,沒有繼承object類的類,稱為經典類。
在Python 3中,預設就是新式類,而在Python2.X中,預設都是是經典類
繼承怎麼減少代碼呢?看例子
class People: def __init__(self,name,age): self.name=name self.age=age def walk(self): print('%s is walkig'%self.name) class Teacher(People): def __init__(self,name,age,level): People.__init__(self,name,age) self.level=level t1=Teacher('zhang',18,10) print(t1.level) #10 print(t1.name) #zhang 子類可以用父類定義的屬性 t1.walk() #zhang is walking 子類無需定義就可以用父類的方法 print(issubclass(Teacher,People)) #True查看Teacher類是不是People類的子類
從上面的例子中可以看到,Teacher類繼承了父類People類,但是Teacher又有自己特有的屬性level,子類也可以定義自己獨有的方法,甚至可以和父類的方法重名,但是執行時會以子類定義的為準。
這就叫做派生
2.組合
繼承是解決什麼‘是’什麼的問題,那還有一種場景就是什麼有什麼,比如老師有生日,學生也有生日,生日有年月日這些屬性,如果每個類都寫的話,又是重覆代碼。但是又不能讓學生和老師繼承生日類。這時就用到了組合。組合就是解決什麼‘有’什麼的問題。看例子
class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell_birth(self): print('出生於%s年%s月%s日'%(self.year,self.mon,self.day)) class Teacher: def __init__(self,name,age,year,mon,day): self.name=name self.age=age self.birth=Date(year,mon,day) t=Teacher('egon',19,2010,10,10) print(t.birth) #<__main__.Date object at 0x0000017E559380F0> t.birth.tell_birth() #出生於2010年10月10日
什麼?嫌參數太多?*args學過吧,你高興就好
1 class Date: 2 def __init__(self,year,mon,day): 3 self.year=year 4 self.mon=mon 5 self.day=day 6 def tell_birth(self): 7 print('出生於%s年%s月%s日'%(self.year,self.mon,self.day)) 8 9 class Teacher: 10 def __init__(self,name,age,*args): 11 self.name=name 12 self.age=age 13 self.birth=Date(*args) 14 t=Teacher('egon',19,2010,10,10) 15 print(t.birth) #<__main__.Date object at 0x0000017E559380F0> 16 t.birth.tell_birth() #出生於2010年10月10日View Code
3.抽象類與介面
繼承有兩種用途:1.代碼重用,子類繼承父類的方法
2.聲明某個子類相容於某父類,定義一個介面類Interface,介面類中定義了一些介面名(就是函數名)且並未實現介面的功能,子類繼承介面類,並且實現介面中的功能
需要註意的是,Python中並沒有介面的關鍵字,我們只能是模仿介面的功能
比如在 Python中,一切皆文件嘛,那程式是文件,硬體是文件,文本文檔也是文件,我們知道什麼叫文件呢,就是能讀能寫,那程式,文本文檔這些,都應該有讀和寫的功能,我們來模擬一下
class Interface: def read(self): pass def write(self): pass class Txt(Interface): def read(self): print('文本文檔的讀取方式') def write(self): print('文本文檔的寫入方式') class Sata(Interface): def read(self): print('硬碟文件的讀取方式') def write(self): print('硬碟文件的寫入方式') class process(Interface): def read(self): print('進程數據的讀取方式') def write(self): print('進程數據的寫入方式')View Code
這麼做的意義就是:我們不需要知道子類有什麼具體的方法,既然他們繼承了文件類,那他們就是文件,那他們就有讀和寫這兩個功能
父類限制了子類子類必須有read和write這兩個方法,而且名字也必須一樣(當然現在只是我們主觀上的限制,一會我們說完抽象類,就可以從代碼級別上限制了),這樣就實現了統一,模擬了介面的概念,這就是歸一化設計。在歸一化設計中,只要是基於一個介面設計的類,那麼所有的這些類實例化出來的對象,在用法上是一樣的
我們再來說一下抽象類:
Python中的抽象類需要導入一個模塊來實現。抽象類只能被繼承,不能被實現
抽象類的寫法:
import abc class File(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass #父類使用了抽象類,那子類就必須繼承父類的方法,而且名字也必須一樣 #這樣就實現了代碼級別的限制 class Txt(File): def read(self): print('文本文檔的讀取方式') def write(self): print('文本文檔的寫入方式')
4.繼承的實現原理
1)繼承順序:
python支持多繼承,當一個類繼承多個父類時,繼承順序是怎樣的呢?這個順序在新式類和經典類中是不一樣的。
在新式類中,繼承順序是廣度優先,在經典類中是深度優先,舉個慄子:
圖不重要,看內容
在這個圖中,H是子類,H繼承E,F,G,E,F,G,又分別繼承B,C,D,B,C,D,同時繼承A
在新式類中的順序是:H E B F C G D A
在經典類中的順序是:H E B A F C G D
2)繼承原理:
當我們定義一個類後,Python就會根據上面的繼承規律解析出一個繼承順序的列表(MRO列表),可以通過mro()查看,但是這個方法只有在新式類中才有,經典類沒有
mro3)super()方法
我們之前用繼承是怎麼用的來著,
class Parent(object): def __init__(self,name,age): self.name=name self.age=age class Child(Parent): def __init__(self,name,age,salary): Parent.__init__(self,name,age,salary) self.salary=salary
這其實是和繼承沒啥關係的寫法,如果父類名字改了,在子類中也要改,更優雅的寫法是用super()
class Parent(object): def __init__(self,name,age): self.name=name self.age=age class Child(Parent): def __init__(self,name,age,salary): super().__init__(name,age) self.salary=salary
這是python3中的寫法,如果是python2,super後面的括弧里要寫(Child,self)
註意:super()方法只適用於新式類
如果是多繼承的關係,就用到mro列表,如果就是要繼承多個父類的方法,那就還是乖乖的用以前指名道姓的方法引用