今日內(nèi)容:
1.繼承介紹
2.繼承與抽象
3.屬性查找
4.繼承的實現(xiàn)原理
5.菱形問題
6.繼承原理
7.深度優(yōu)先與廣度優(yōu)先
8.python的mixins機(jī)制
一、繼承介紹
1.繼承:繼承是一種新建類的方式鼎文,新建的類稱之為子類解取,被繼承的類稱為父類吉捶,也成為基類與超類
2.為何要用繼承:子類會遺傳父類的屬性(與方法),所以繼承是解決類與類之間代碼冗余的問題
還記得我們?yōu)槭裁磿x出類嗎,我們學(xué)習(xí)的編程方法叫做面向?qū)ο缶幊掏崃幔皇敲嫦蝾惥幊蹋乔皫滋扉_始學(xué)習(xí)時我們的類是為什么創(chuàng)建出來的呢掷匠?
當(dāng)我們創(chuàng)建對象時滥崩,發(fā)現(xiàn)很多對象都會有相同的特征,如名字讹语,性別钙皮,年齡等,每創(chuàng)建一個對象我們就要寫重復(fù)代碼,所以類是為了減少對象與對象之間代碼冗余的問題
3.如何繼承:
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1):
pass
class Sub2(Parent1,Parent2):
pass
print(Sub1.__bases__)
print(Sub2.__bases__)
# 繼承案列:
class OldboyPeople:
school = "oldboy"
class Student(OldboyPeople):
def __init__(self,name,age,gender,stud_id,course):
self.name = name
self.age = age
self.gender = gender
self.stu_id = stud_id
self.course = course
def choose(self):
print('%s 正在選課' %self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,gender,salary,level):
self.name = name
self.age = age
self.gender = gender
self.salary = salary
self.level = level
def score(self,stu,num):
stu.num = num
stu1=Student("艾利克斯",73,'male',1001,"python全棧開放")
tea1=Teacher("egon",18,'male',2000,10)
print(stu1.school)
二株灸、繼承與抽象
名字很高大上崇摄,其實就是為了幫助我們理解為什么要有父類。抽象的意思慌烧,就是我們從不同的對象總結(jié)相同特點逐抑,創(chuàng)建一個類,之后再根據(jù)類的相同特征再找出一個父類屹蚊,如人可以是一個類厕氨,貓可以是一個類,狗可以是一個類汹粤,我們還可以根據(jù)這三個類總結(jié)一個動物類命斧。這個過程就是抽象的過程。
而當(dāng)我們根據(jù)以上的關(guān)系根據(jù)三個類實例化出對象嘱兼,那么動物類就是人国葬,貓,狗的父類芹壕。
三汇四、屬性查找:有了繼承關(guān)系,對象在查找屬性時踢涌,先從對象自己的dict中找通孽,如果沒有則去子類中找,然后再去父類中找……
因為屬性查找的順序睁壁,所以當(dāng)子類與父類都有同一個方法名(但是功能不同)時背苦,總是會優(yōu)先查找子類中的方法,就產(chǎn)生了一個覆蓋的效果潘明。
若父類不想讓子類調(diào)用自己的方法行剂,可以利用隱藏屬性,加入雙下劃線钳降,這就表示這個方法只能在本類中調(diào)用
# 在子類派生的新方法中重用父類的功能
# 方式一:指名道姓地調(diào)用某一個類的函數(shù)
# 特點:不依賴于繼承關(guān)系
#
class OldboyPeople:
school = "oldboy"
# 空對象,"艾利克斯",73,'male'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def f1(self):
print('1111111')
class Student(OldboyPeople):
# 空對象,"艾利克斯",73,'male',1001,"python全棧開放"
def __init__(self,name,age,gender,stu_id,course):
OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空對象,"艾利克斯",73,'male')
self.stu_id = stu_id
self.course = course
def choose(self):
print('%s 正在選課' %self.name)
def f1(self):
OldboyPeople.f1(self)
print("22222")
class Teacher(OldboyPeople):
def score(self,stu,num):
stu.num = num
stu1=Student("艾利克斯",73,'male',1001,"python全棧開放")
# tea1=Teacher("egon",18,'male',2000,10)
stu1.f1()
四厚宰、繼承的實現(xiàn)原理
新式類:繼承了Object類的子類以及他的所有子類,都稱為新式類
經(jīng)典類:沒有繼承Object類的任何類都稱為經(jīng)典類
在python3中牲阁,做了處理固阁,所有的類都是新式類。即如果你在定義一個類的時候城菊,沒有繼承其他類备燃,python3會默認(rèn)幫你繼承Object類,在python2中并無此處理,所以python2中既有經(jīng)典類也有新式類凌唬。
若你寫的類想要兼容python2與python3并齐,只要在定義類時,繼承Object就可以了
五、菱形問題
菱形問題:一個子類繼承的多條分支最終匯聚到一個不是Object的類况褪,注意撕贞,菱形問題不是一個好事情,在你的項目中一定要盡可能的避免這個問題
如果繼承關(guān)系為菱形結(jié)構(gòu)测垛,即子類的父類最后繼承了同一個類捏膨,那么屬性的查找方式有兩種:
經(jīng)典類:深度優(yōu)先
新式類:廣度優(yōu)先
深度優(yōu)先演示圖:
廣度優(yōu)先演示圖:
C3算法與mro()方法介紹(了解即可)
python到底是如何實現(xiàn)繼承的,對于你定義的每一個類食侮,python會計算出一個方法解析順序(MRO)列表号涯,這個MRO列表就是一個簡單的所有基類的線性順序列表,如:
print(A.mro()) # A.__mro__
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
for i in A.mro():
print(i)
<class '__main__.A'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.F'>
<class '__main__.D'>
<class '__main__.G'>
<class 'object'>
六锯七、Pyton Mixins機(jī)制
首先這不是一個語法链快,而更像是一個python程序員之間的約定
Python既然設(shè)計了可以多繼承,那么多繼承一定有他便利的地方眉尸,最了然的就是我們可以繼承多個類域蜗,減少代碼冗余。但是多繼承又容易產(chǎn)生菱形問題噪猾,并且和我們以往的繼承思想有所不同霉祸。
人的世界觀里繼承應(yīng)該是個”is-a”關(guān)系。 比如轎車類之所以可以繼承交通工具類畏妖,是因為基于人的世界觀脉执,我們可以說:轎車是一個(“is-a”)交通工具疼阔,而在人的世界觀里戒劫,一個物品不可能是多種不同的東西,因此多重繼承在人的世界觀里是說不通的婆廊。
但是在我們的程序設(shè)計中迅细,又是可能會出現(xiàn)一個對象卻是需要繼承多個類。如下例子:
'''
民航飛機(jī)淘邻、直升飛機(jī)茵典、轎車都是一個(is-a)交通工具,前兩者都有一個功能
是飛行fly宾舅,但是轎車沒有统阿,所以如下所示我們把飛行功能放到交通工具這個
父類中是不合理的
'''
class Vehicle: # 交通工具
def fly(self):
'''
飛行功能相應(yīng)的代碼
'''
print("I am flying")
class CivilAircraft(Vehicle): # 民航飛機(jī)
pass
class Helicopter(Vehicle): # 直升飛機(jī)
pass
class Car(Vehicle): # 汽車并不會飛,但按照上述繼承關(guān)系筹我,汽車也能飛了
pass
但是如果民航飛機(jī)和直升機(jī)都各自寫自己的飛行fly方法扶平,又違背了代碼盡可能重用的原則(如果以后飛行工具越來越多,那會重復(fù)代碼將會越來越多)蔬蕊。
?怎么辦结澄???為了盡可能地重用代碼麻献,那就只好在定義出一個飛行器的類们妥,然后讓民航飛機(jī)和直升飛機(jī)同時繼承交通工具以及飛行器兩個父類,這樣就出現(xiàn)了多重繼承勉吻。這時又違背了繼承必須是”is-a”關(guān)系监婶。這個難題該怎么解決虱痕?
Python提供了Mixins機(jī)制纲熏,簡單來說Mixins機(jī)制指的是子類混合(mixin)不同類的功能,而這些類采用統(tǒng)一的命名規(guī)范(例如Mixin后綴)苍息,以此標(biāo)識這些類只是用來混合功能的源譬,并不是用來標(biāo)識子類的從屬"is-a"關(guān)系的集惋,所以Mixins機(jī)制本質(zhì)仍是多繼承,但同樣遵守”is-a”關(guān)系踩娘,如下
class Vehicle: # 交通工具
pass
class FlyableMixin:
def fly(self):
'''
飛行功能相應(yīng)的代碼
'''
print("I am flying")
class CivilAircraft(FlyableMixin, Vehicle): # 民航飛機(jī)
pass
class Helicopter(FlyableMixin, Vehicle): # 直升飛機(jī)
pass
class Car(Vehicle): # 汽車
pass
# ps: 采用某種規(guī)范(如命名規(guī)范)來解決具體的問題是python慣用的套路
使用Mixin類實現(xiàn)多重繼承要非常小心
首先它必須表示某一種功能刮刑,而不是某個物品,python 對于mixin類的命名方式一般以 Mixin, able, ible 為后綴
其次它必須責(zé)任單一养渴,如果有多個功能雷绢,那就寫多個Mixin類,一個類可以繼承多個Mixin理卑,為了保證遵循繼承的“is-a”原則翘紊,只能繼承一個標(biāo)識其歸屬含義的父類
然后,它不依賴于子類的實現(xiàn)
最后藐唠,子類即便沒有繼承這個Mixin類帆疟,也照樣可以工作,就是缺少了某個功能宇立。(比如飛機(jī)照樣可以載客踪宠,就是不能飛了)