【python系統學習14】類的繼承與創新
目錄:
- 目錄:
[toc]
- 類的繼承
- 子類和父類
- 繼承的寫法
- 繼承示例
- 父類可以被無限個子類所繼承
- 子類例項可呼叫父類屬性和方法
- 類的始祖(根類)
- 根類 - object
- 例項歸屬判斷 - isinstance()
- 類的繼承升級版 - 多層繼承
- 1、啥是多層繼承
- 2、虛擬碼
- 3、示例程式碼
- 4、多層繼承的好處
- 類的繼承升級版 - 多重繼承
- 1、啥是多重繼承
- 2、就近繼承
- 3、爸爸近還是爺爺近
- 4、多重繼承的示例
- 5、多重繼承的作用
- 多層繼承和多重繼承
- 二者的比較
- 二者的結合
- 類的創新
- 創新 - 新增程式碼
- 創新 - 修改(重寫)繼承的內容
- 優雅的重寫
- 子類和父類
- 繼承的寫法
- 繼承示例
- 父類可以被無限個子類所繼承
- 子類例項可呼叫父類屬性和方法
- 根類 - object
- 例項歸屬判斷 - isinstance()
- 1、啥是多層繼承
- 2、虛擬碼
- 3、示例程式碼
- 4、多層繼承的好處
- 1、啥是多重繼承
- 2、就近繼承
- 3、爸爸近還是爺爺近
- 4、多重繼承的示例
- 5、多重繼承的作用
- 二者的比較
- 二者的結合
- 創新 - 新增程式碼
- 創新 - 修改(重寫)繼承的內容
- 優雅的重寫
初中學政治我們就學到過,要繼承中華民族的優秀文化、又要在繼承的基礎上創新。
文化是在不斷繼承和創新中發展的,程式碼也是。
我們可以用類特有的繼承方法和拓展創新功能,實現程式碼層面的前進。
此節這兩個知識點屬於類中較高階的操作,讓用類寫成的程式碼更容易複用、拓展和維護。
類的繼承
說道“繼承”這倆字,你能想到啥?
反正我想到的是,兒子繼承老子
的姓氏籍貫特徵和遺產等。
放到程式碼裡,就是子類繼承父類
的屬性和方法。
也就是說,通過類的繼承,可以讓子類擁有父類擁有的所有屬性和方法。
這樣,很多基礎程式碼不用再重複書寫,就可以直接實現程式碼的複用
子類和父類
那之前只瞭解到類的概念,怎麼又多出來一個“子類”和“父類”的區分呢?
我想,這只是統稱。凡是一個類A繼承了另一個類B,那麼A就是B的兒子,A就可以統稱為子類,B就可以統稱為父類。
題外話,我現在看這個“類”字看多了,咋看咋像“糞”。。。
繼承的寫法
虛擬碼
class A(B):
...子類A的創新定製程式碼 # 下詳
注意:
A就是子類,B就是父類 子類繼承父類時,只需要子類右邊的小括號內部寫父類的類名即可。 小括號和冒號都是英文格式
繼承示例
還是以老子和兒子的身份,編寫兩段語義化的程式碼來了解下:
# 子類Son繼承父類Father的示意
class Father:
familyName = '郭'
nativePlace = '河北省'
def language(self):
print('說中國話')
class Son(Father): # 子類Son繼承父類Father的寫法
def __init__(self, name):
self.name = name
def secondLanguage(self):
self.language()
print('學說了英語')
父類可以被無限個子類所繼承
不是一個Son類可以繼承Father,再來十個Son也可以。
class Father:
familyName = '郭' # 表示姓氏
nativePlace = '河北省' # 表示籍貫
def language(self): # 表示說母語的能力
print('說中國話')
class Son(Father): # 子類Son繼承父類Father
def __init__(self, name):
self.name = name
def secondLanguage(self):
self.language()
print('學說了英語')
# 父類可以被無限個子類所繼承
class Son2(Father): # 子類Son2繼承父類Father
def __init__(self):
print('這裡自己想象點Son2特色的程式碼吧,我絞盡腦汁了~')
def characterFn(self):
self.language()
pass
class SonN(Father):
pass
# ...
子類例項可呼叫父類屬性和方法
子類繼承來的屬性和方法,也會傳遞給子類建立的例項。
這很好理解,子類繼承父類的屬性和方法,相當於子類外掛了父類的屬性和方法。那麼子類的例項,也就可以用這些屬性和方法。
看個例子:
class Father:
familyName = '郭' # 表示姓氏
nativePlace = '河北省' # 表示籍貫
def language(self): # 表示說母語的能力
print('說中國話')
class Son(Father): # 子類Son繼承父類Father
def __init__(self, name):
self.name = name
def secondLanguage(self):
self.language()
print('學說了英語')
son1 = Son('小菊')
print(son1.name + '姓' + son1.familyName)
print(son1.name + '籍貫是' + son1.nativePlace)
# 列印結果:
# 小菊姓郭
# 小菊籍貫是河北省
例項化後的物件son1不僅有類Son內部定義的屬性,還有父類Father內部定義的屬性familyName、nativePlace等。
說明子類建立的例項,從子類那間接得到了父類的所有屬性和方法,例項化物件可以隨便運用。
類的始祖(根類)
根類 - object
在上邊類的繼承中,我們協議子類是Son、父類是Father。
那Father還有他的父類嗎?
答案是有的,那就是object
。
object
是所有類的父類,我們將其稱為根類(可理解為類的始祖)。
上述程式碼中,class Father:
相當於寫成class Father(object)
用isinstance來判斷說明:
例項歸屬判斷 - isinstance()
函式isinstance(),可以用來判斷某個例項是否屬於某個類。
觀察程式碼看用法:
# 例項歸屬判斷 isinstance()
class Father:
pass
class Son(Father):
pass
class Son2:
pass
son1 = Son()
print(1,isinstance(son1,Son)) # True
# 說明:子類建立的例項屬於子類(廢話。。。)
print(2,isinstance(son1,Father)) # True
# 說明:子類建立的例項同時也屬於父類
print(3,isinstance(son1,object)) # True
# 說明:說明任何類建立的例項都屬於根類object
print(4,isinstance(son1,(Son,Father))) # True
# 說明:son1屬於Son的例項、也屬於Father的例項
print(4,isinstance(son1,(Father,Son))) # True
# 說明:son1屬於Son的例項、也屬於Father的例項
print(5,isinstance(son1,Son2)) # False
# 說明:son1不是Son2的例項。A子類建立的例項不屬於C子類
print(6,isinstance(Son,Father)) # False
# 說明:這倆都是類所以報錯。
print(7,isinstance(Son,object)) # True
# 說明:可以說明Son(任何類)是根類object的例項
print(8,isinstance(Father,object)) # True
# 說明:可以說明Father(任何類)是根類object的例項
print(9,isinstance(Son,(Father,object))) # True
# 說明:Son不屬於Father的例項,但他是根類object的例項
總結用法:
# 第一種(虛擬碼)
isinstance(例項名, 類名)
# 第二種(虛擬碼)
isinstance(例項名, (類名1, 類名2...)) # 第二個引數是類名組成的元祖型別資料。
返回結果:
布林值。True或False。
判斷第一個引數是第二個引數的例項、或者判斷第一個引數屬於第二個引數中某個類的例項,則返回True。反之返回False。
總結知識點:
1. 子類建立的例項,同屬於該子類的父類。
2. 父類建立的例項,與他的子類沒關係。
3. 所有類,都屬於根類object的例項。
4. 所有例項,都屬於根類object的例項。
5. 子類A建立的例項,不屬於其他C等子類的例項。
具體值與基本型別的歸屬判斷: 看程式碼總結知識點吧
print(isinstance(1,int)) # True
# 判斷1是否為整數類的例項,例項1是整數類的例項
print(isinstance(1,str)) # False
# 例項1不是字串類的例項
print(isinstance(1,(int,str))) # True
# 例項1是整數類的例項
print(isinstance(1,object)) # True
# 例項1是根類object的例項
類的繼承升級版 - 多層繼承
1、啥是多層繼承
繼承不僅可以發生在兩個層級之間(即父類-子類),還可以有父類的父類、父類的父類的父類……
比如三個類A、B、C。A是爺爺、B是爸爸、C是兒子。我們可以B繼承A、C再繼承B。後邊再可以有孫子D、D再繼承C等等。。。
這樣一層一層,層層向上/向下繼承,就是多層繼承
。
一“碼”以蔽之:
2、虛擬碼
class B(A):
...
class C(B):
...
class D(C):
...
class...
3、示例程式碼
# 多層繼承
class Grand:
familyName = '郭' # 表示姓氏
class Father(Grand):
fatherName = '明爸'
class Son(Father):
def __init__(self,name):
self.sonName = name
print(self.fatherName) # 子類拿到父類的屬性
print(self.familyName+self.sonName) # 子類拿到父類的父類 的屬性
son = Son('小明')
# 執行後列印
# 明爸
# 郭小明
print(son.fatherName) # 例項拿到父類的屬性
print(son.familyName + son.sonName) # 例項拿到父類的父類 的屬性
# 執行後列印
# 明爸
# 郭小明
只要你願意,你可以繼續拓展上面的例子,或往上(爺爺的爸爸),或往下(兒子的兒子)。所以,多層繼承又屬於繼承的深度拓展。
4、多層繼承的好處
從上邊程式碼就能看出功能,Son類建立的例項不僅可以呼叫Father的屬性和方法、還能呼叫爺爺Grand類的屬性和方法。
結論就是:子類建立的例項可呼叫所有層級父類的屬性和方法。
類的繼承升級版 - 多重繼承
1、啥是多重繼承
一個類同時繼承多個類,就叫多重繼承
。就好像同時擁有好幾個爸爸。
# 虛擬碼:
class A(B,C,D):
2、就近繼承
括號裡B、C和D的順序是有講究的。和子類更相關的父類會放在更左側。
也就是血緣關係越深的爸爸,在括號裡的順序越靠前。
既然跟B爸爸最親,所以兒子A類建立的例項在呼叫屬性和方法時,會優先在最左側的父類B中找,找不到才會去第二左側的父類C中找,依舊找不到才會最後去父類D中找。有點“就近原則”的意思,當然,如果最後幾個類中都沒找到,
3、爸爸近還是爺爺近
A類建立的例項呼叫屬性和方法時,先在直接爸爸A類中找,還是先在爸爸繼承的最親近的B類中找?(這裡A相當於A例項的爸爸,B相當於A例項的爺爺。)
class Grand:
name = '親爺爺'
class Father(Grand):
name = '爸爸'
son = Father()
# 說明先在直接爸爸中找屬性
print(son.name) # 爸爸
總結:先在直接爸爸中找屬性。找不到,再去爸爸繼承的爺爺類們找。
4、多重繼承的示例
用一段大型家庭倫理來寫一個多重繼承。
其中,身份情況如下:
代表的身份 | 倫理身份 | 對應下邊示例中的變數 |
---|---|---|
例項 | 兒子 | son |
A類 | 爸爸 | Father |
B類 | 親爺爺 | 爺爺Grand2在三個爺爺中排行老二 |
C類 | 大爺爺 | Grand1 |
D類 | 小爺爺 | Grand3 |
# 多重繼承
class Grand1:
name = '大爺爺'
grand1 = '我是老大'
age = 60
class Grand2:
name = '親爺爺'
grand2 = '我是老二'
age = 59
class Grand3:
name = '小爺爺'
grand3 = '我是老三'
age = 58
hobby = '只有小爺爺有hobby屬性'
class Father(Grand2, Grand1, Grand3):
name = '爸爸'
father = '我是爸爸'
son = Father()
# 說明先在直接爸爸中找屬性
print(son.name) # 爸爸
# 以下說明例項可以拿到其他重繼承類內部的屬性
print(son.father) # 我是爸爸
print(son.grand1) # 我是老大
print(son.grand2) # 我是老二
print(son.grand3) # 我是老三
# 以下說明多重繼承的就近原則 - 就近取最近的爺爺的屬性
print(son.age) # 59
# 以下說明前幾重父類都沒有,取第一個有該屬性的父類,哪怕這個父類時最後一重繼承的
print(son.hobby) # 只有小爺爺有hobby屬性
5、多重繼承的作用
這自然不用說,子類建立的例項可呼叫所有重父類的屬性和方法。
多層繼承和多重繼承
二者的比較
來一個比較表格吧
名稱 | 多層繼承 | 多重繼承 |
---|---|---|
寫法 | class B(A): ... class C(B): ... |
class A(B, C, D): ... |
例子 | 孫子繼承兒子、兒子繼承爸爸、爸爸繼承爺爺 | 爸爸繼承爺爺、大爺爺、小爺爺 |
特點 | 類在縱向上深度拓展 | 類在橫向上寬度擴充套件 |
作用 | 子類建立的例項,可呼叫所有層級的父類的屬性和方法 | 同多層繼承,可以呼叫所有。不過遵循就近原則。優先考慮靠近子類的父類的屬性和方法。 |
二者的結合
多層繼承和多重繼承二者單用都是一條線,但是將二者結合起來,那就是個十字啊!
例項兒子依次繼承了爸爸、爺爺、大爺爺、小爺爺、爺爺的爸爸這幾個類的所有屬性和方法。簡直就是超級富二代
啊~
多層繼承和多重繼承的結合,讓繼承的類擁有更多的屬性和方法,且能更靈活地呼叫。進而,繼承的力量也得以放大了很多倍。
一碼以蔽之
# 多層繼承和多重繼承結合
class GrandFather:
name = '爺爺的爸爸-太爺爺'
age = 102
class Grand1:
name = '大爺爺'
class Grand2(GrandFather): # 多層繼承1
name = '親爺爺'
class Grand3:
name = '小爺爺(又名二爺爺)'
age = 62
class Father(Grand2, Grand1, Grand3): # 多層繼承2 + 多重繼承
name = '爸爸'
son = Father() # 例項兒子
print(son.name) # 爸爸
print(son.age) # 102
上例中,son.age
這段列印的 102說明,雖然Grand2中沒有,但是Grand2的多層繼承GrandFather中有,所以順序是先從多層中找,最後才考慮多重繼承的Grand3中的變數。
總結:多重繼承中,若某父類還有父類的話,會先按照多層繼承的順序,縱向往上找到頂。若到頂還沒有,則再繼續向右擴充套件尋找多重的繼承。
類的創新
我們可以在繼承父類程式碼的基礎上,再書寫子類自己更具自我特色的程式碼。這就是子類的創新部分了。
比如下邊程式碼中,兒子的姓就是繼承父親的。兒子的名字就是自己創新的部分。兒子籍貫是繼承父類後生下來就有的,但是以後自己跑到北京、杭州居住,那就是自己創新的部分。
創新 - 新增程式碼
# 子類繼承父類並做自我創新
class Father:
familyName = '郭' # 姓氏
nativePlace = '河北省' # 籍貫
def language(self):
print('%s家,母語說中國話' %(self.familyName))
class Son(Father): # 子類Son繼承父類Father
def __init__(self, name, presentAddress):
self.name = name # 子類創新自己的屬性name
self.presentAddress = presentAddress # 子類創新自己的屬性presentAddress
def secondLanguage(self, languageName): # 子類創新自己的方法secondLanguage
self.language()
print('%s單獨學說了%s' %(self.name, languageName))
def resume(self): # 子類創新自己的方法resume
print('%s姓%s,籍貫是%s。現居住在%s' %(self.name, self.familyName,self.nativePlace, self.presentAddress))
# 子類的第一個例項
son1 = Son('小菊', '北京')
son1.secondLanguage('英語')
son1.resume()
# 子類的第二個例項
son2 = Son('小鋒', '杭州')
son2.secondLanguage('韓語')
son2.resume()
# 例項既可以用子類的屬性和方法,也可以呼叫父類的屬性和方法
print(son1.familyName)
print(son1.nativePlace)
上述程式碼中,
子類內部用的self.language()、以及這個language方法內部用的屬性familyName、子類自己方法resume中用的self.nativePlace等都是繼承自父類Father的。
這就是繼承的部分。
Son類中的self.name、self.presentAddress 是屬於子類新增的屬於自己的屬性、
self.secondLanguage、self.resume 是屬於子類新增的屬於自己的方法。這就是創新的部分。
好吧,我承認這是一句多餘的不行的廢話
創新 - 修改(重寫)繼承的內容
在繼承的基礎上可以做新加程式碼,甚至可以重寫(修改)繼承來的屬性和方法。
重寫程式碼
: 是在子類中,對父類程式碼的修改。
還是用程式碼來說明問題,在上邊程式碼的基礎上,我們想一段情景劇:
Father類現在有一個二兒子Son2,他從小生下來被過繼給了日本的親叔叔。
因為叔叔和爸爸同姓,所以繼承的屬性“familyName”不用管,還是姓“郭”。但是到了日本後上戶口就是日本的籍貫了,所以“nativePlace”得修改重寫。
另外雖然是爸爸的兒子(繼承自Father),但是因為在日本長大,所以母語變成說日語了。於是我們重寫了繼承來自爸爸類的方法“language”。如下
class Father:
familyName = '郭'
nativePlace = '河北省'
def language(self):
print('%s家,母語說中國話' %(self.familyName))
class Son(Father): # 子類Son繼承父類Father
# ...程式碼同上一段裡的
class Son2(Father): # 繼承父類Father,不過被過繼給叔叔的二兒子類Son2
languageTxt = '日語' # 自己的屬性,屬新增的創新
nativePlace = '北海道' # 修改的屬性,屬重寫的創新
def language(self): # 修改的方法,屬重寫的創新
print('我的母語說:', self.languageTxt) # 裡邊的程式碼有自己定製的內容,屬於重寫。
son2 = Son2()
son2.language()
# 我的母語說: 日語
可見,重寫父類的屬性和方法,只要在子類中重新定義同名的變數,並做自己的定製程式碼即可。
但是,直接在Son2裡這麼寫,Father會不會傷心呢?
到底是自己生的兒子,給了別人後就這麼迫不及待、直接的修改,爸爸的心,痛啊!
所以,重寫程式碼按照上邊這麼寫,他粗魯了。那有什麼優雅的重寫嗎?
優雅的重寫
# 優雅的重寫
class Father:
familyName = '郭'
nativePlace = '河北省'
def language(self, languageTxt = '中國話'): # 需要父類的這個方法協助修改
print('%s家,母語說%s' %(self.familyName, languageTxt))
class Son2(Father):
languageTxt = '日語'
nativePlace = '北海道'
def language(self): # 依舊重寫方法
Father.language(self, self.languageTxt) # 不過裡邊的程式碼不再是粗魯的直接修改為自己的內容,而是呼叫父類的方法,但是傳不同的引數。
son2 = Son2()
son2.language()
# 郭家,母語說日語
上邊程式碼,依舊重寫方法。不過裡邊的程式碼不再是粗魯的直接修改為自己的內容,而是呼叫父類的方法,但是傳不同的引數。
總結:所謂創新,在複用程式碼的基礎上,又能滿足個性化的需求。
本文使用 mdnice 排版