類的繼承與派生
經典類和新式類
在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 硬盤數據的寫入方法
