Python基礎----繼承派生、組合、接口和抽象類
經典類和新式類
在python3中,所有類默認繼承object,但凡是繼承了object類的子類,以及該子類的子類,都稱為新式類(在python3中所有的類都是新式類)
沒有繼承object類的子類成為經典類(在python2中,沒有繼承object的類,以及它的子類,都是經典類)
1 class People: 2 pass 3 class Animal: 4 pass 5 class Student(People,Animal): #People、Animal稱為基類或父類,Student為子類,Student繼承了People和Animal的所有屬性 6 pass 7 print(Student.__bases__) #__bases__方法,查看繼承的類的元組 8 print(People.__bases__) 9 print(Animal.__bases__)
輸出結果:
1 (<class ‘__main__.People‘>, <class ‘__main__.Animal‘>) #繼承了兩個父類 2 (<class ‘object‘>,) #默認繼承了object類 3 (<class ‘object‘>,)
繼承
繼承是為了減少代碼重用的問題,以減少代碼冗余。
繼承是一種是什麽是什麽的關系,例如老師類是人類,而非老師類是生日類
繼承類示例:
1 class People: #定義父類People 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 def walk(self): 6 print(‘%s is walking‘ %self.name) 7 8 #Teacher類和Student類無任何屬性 9 class Teacher(People): #Teacher類繼承People類的屬性 10 pass 11 class Student(People): #Student類繼承People類的屬性 12 pass
引用測試:
1 t=Teacher(‘bob‘,18) #實例化一個Teacher對象,而非People對象,Student子類同理 2 print(type(t)) 3 print(t.name,t.age) 4 print(t.__dict__) 5 t.walk() #Teacher子類繼承了People的屬性,使得Teacher子類的對象能夠調用到父類的屬性 6 7 輸出結果: 8 <class ‘__main__.Teacher‘> 9 bob 18 10 {‘name‘: ‘bob‘, ‘age‘: 18} 11 bob is walking
派生
派生是在子類繼承父類的基礎上, 定義子類獨有的屬性,例如Teacher可以有教師等級的劃分、有教學課程的劃分,但是繼承父類People類是沒有等級和課程的劃分的。
示例:
1 #定義父類People 2 class People: 3 def __init__(self, name, age,sex): 4 self.name = name 5 self.age = age 6 self.sex=sex 7 def walk(self): 8 print(‘%s is walking‘ % self.name) 9 def test(self): 10 print(‘test class from father class %s‘ %self.name) 11 #定義Teacher子類 12 class Teacher(People): 13 school = ‘jialidun‘ 14 def __init__(self, name, age,sex,level,salary): 15 People.__init__(self,name,age,sex) #繼承父類的初始化內容,實例化時候接收的參數name、age、sex會傳給People.__init__ 16 self.level=level #派生的獨有屬性 17 self.salary=salary #派生的獨有屬性 18 def teach(self): #派生的獨有屬性 19 print(‘%s is teaching‘ %self.name) 20 def test(self): #派生父類的已有屬性,對象在進行屬性引用的時候會優先引用實例化過程中用到的類 21 People.test(self) 22 print(‘from teacher‘) 23 #定義Student子類 24 class Student(People): 25 def __init__(self, name, age,sex,group): 26 People.__init__(self, name, age, sex) 27 self.group=group 28 def study(self): 29 print(‘%s is studying‘ %self.name)
測試驗證:
1 t=Teacher(‘natasha‘,18,‘male‘,10,3000) #__init__(t,‘natasha‘,18,‘male‘,10,3000) 2 print(Teacher.__bases__) 3 print(Teacher.__dict__) 4 t.test()
組合
不同於繼承,組合是包含的意思,表示一種什麽有什麽的關系,也是為了減少重復代碼的
示例:還是People、Teacher和Student的例子,只是加上了一個Birthday生日類
1 #Birthday類,需要傳入年月日 2 class Birthday: 3 def __init__(self,year,mon,day): 4 self.year=year 5 self.mon=mon 6 self.day=day 7 def tell_birth(self): 8 print(‘出生於<%s>年 <%s>月 <%s>日‘ % (self.year,self.mon,self.day)) 9 #People類,需要接受名字年齡年月日,年月日傳給Birthday類 10 class People: 11 def __init__(self, name, age, year, mon, day): 12 self.name = name 13 self.age = age 14 #__init__接收的year, mon, day傳給Birthday類 15 self.birth = Birthday(year, mon, day) #包含Birthday類,生日不只是人類才有,其他動物也可以有生日,不同於繼承 16 def walk(self): 17 print(‘%s is walking‘ % self.name) 18 #Teacher類 19 class Teacher(People): 20 def __init__(self, name, age, year, mon, day,level,salary): 21 #__init__接收的name, age, year, mon, day傳給People類 22 People.__init__(self,name,age,year,mon,day) 23 self.level=level 24 self.salary=salary 25 def teach(self): 26 print(‘%s is teaching‘ %self.name) 27 #Student類 28 class Student(People): 29 def __init__(self, name, age, year, mon, day,group): 30 People.__init__(self,name,age,year,mon,day) 31 self.group=group 32 def study(self): 33 print(‘%s is studying‘ %self.name)
測試驗證:
1 t=Teacher(‘hurry‘,18,1990,2,33,10,3000) #傳入的值為Teacher類接收的值 2 print(t.name,t.age) #對象t的名字和年齡 3 print(t.birth) #輸出的是一個類對象,因為父類People定義的birth屬性就是一個類Birthday 4 t.birth.tell_birth() #查看對象t所繼承的People類的birth屬性(Birthday類)的tell_birth()屬性 5 print(t.birth.year) 6 print(t.birth.mon) 7 print(t.birth.day)
接口和抽象類
接口
接口是一組功能的入口,要調用某一組功能,需要通過接口來進行調用,而不需要關註這組功能是如何實現的,要的只是結果。
在類裏,接口是提取了一群類共同的函數,可以把接口當做一個函數的集合。
python模仿接口示例:
1 #模仿Linux內文件讀寫的接口,Linux不管是文本,還是磁盤還是進程都是通過文件去實現的,只不過方法不同,但是沒關系 2 class File: #定義一個接口類,提供read和write方法,但是一定是pass沒有處理過程的,因為功能的實現具體靠的是子類 3 def read(self): #定接口函數read 4 pass 5 def write(self): #定義接口函數write 6 pass 7 #定義子類實現讀寫功能 8 #文本文件的讀寫 9 class Txt(File): #文本,具體實現read和write 10 def du(self): #註意並不是read 11 print(‘文本數據的讀取方法‘) 12 def xie(self): #註意並不是write 13 print(‘文本數據的寫入方法‘) 14 #硬盤數據的讀寫 15 class Sata(File): #磁盤,具體實現read和write 16 def read(self): 17 print(‘硬盤數據的讀取方法‘) 18 def write(self): 19 print(‘硬盤數據的寫入方法‘) 20 #進程數據的讀寫 21 class Process(File): 22 def read(self): 23 print(‘進程數據的讀取方法‘) 24 def write(self): 25 print(‘進程數據的寫入方法‘)
測試驗證:硬盤和進程一樣,所以制作文本和硬盤的測試即可
硬盤讀寫測試:
1 disk=Sata() #實例化一個硬盤讀寫對象 2 disk.read() #硬盤讀 3 disk.write() #硬盤寫 4 5 輸出結果: 6 硬盤數據的讀取方法 7 硬盤數據的寫入方法
文本讀寫測試:執行後會發現沒有任何輸出,那是因為txt對象實際上訪問的read和write屬性並非子類Txt所提供的屬性,Txt所提供的屬性只是du和xie,但是txt對象有read和write屬性,別忘了Txt類是繼承了父類File的屬性,所以實際上txt對象的read和write屬性是父類File提供的
1 txt=Txt() 2 txt.read() 3 txt.write()
正確的做法是將Txt類的du和xie方法改成read和write方法,這麽做的意義為歸一化
歸一化,讓使用者無需關心對象的類是什麽,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。
抽象類
抽象類的本質上也是類,但是抽象類只能夠被繼承,不能進行實例化,也就是說可以當父類,但是不能生成對象。
抽象類介於接口和歸一化中間,用於實現接口的歸一化
當子類繼承抽象類的時候,如果抽象類定義了抽象方法,那麽子類必須要定義同名的方法。即父類限制:
1、子類必須要有父類的方法
2、子類實現的方法必須跟父類的方法的名字一樣
python的抽象類通過abc模塊實現。
接口歸一化示例:
1 import abc 2 class File(metaclass=abc.ABCMeta): #metaclass指的是元類,邊會講,現在只需記住這個詞 3 @abc.abstractmethod #抽象方法,即一個裝飾器裝飾read屬性 4 def read(self): 5 pass 6 @abc.abstractmethod #抽象方法,即一個裝飾器裝飾write屬性 7 def write(self): 8 pass 9 # # 當繼承File類時候,如果沒有read和write方法,會提示出錯TypeError: Can‘t instantiate abstract class Txt with abstract methods read, write 10 # class Txt(File): 11 # def du(self): 12 # print(‘文本數據的讀取方法‘) 13 # def xie(self): 14 # print(‘文本數據的寫入方法‘) 15 #定義子類具體實現文本的讀寫操作 16 class Txt(File): 17 def read(self): 18 print(‘文本數據的讀取方法‘) 19 def write(self): 20 print(‘文本數據的寫入方法‘) 21 #定義子類具體實現硬盤的讀寫操作 22 class Sata(File): 23 def read(self): 24 print(‘硬盤數據的讀取方法‘) 25 def write(self): 26 print(‘硬盤數據的寫入方法‘) 27 #定義子類具體實現進程的讀寫操作 28 class Process(File): 29 def read(self): 30 print(‘進程數據的讀取方法‘) 31 def write(self): 32 print(‘進程數據的寫入方法‘)
測試驗證:
1 t=Txt() 2 t.read() 3 t.write() 4 s=Sata() 5 s.read() 6 s.write() 7 輸出結果: 8 文本數據的讀取方法 9 文本數據的寫入方法 10 硬盤數據的讀取方法 11 硬盤數據的寫入方法
Python基礎----繼承派生、組合、接口和抽象類