這一章節主要講解面向對象進階程式設計->繼承進階篇,包括類多繼承介紹和繼承經典類和新式類屬性的查找順序不同之處。
多繼承
上一章節我們講到繼承,子類繼承父類,可以擁有父類的屬性和方法,也可以進行擴充。但是有時候會發現,子類需要繼承一個類的方法,又要繼承另一個類的方法,才能完成要實作的功能。怎麼辦?python給我們提供了多繼承的概念。類似于C++語言,俗稱類的多繼承。
看個例子:
>>> class Animal(object):
def __init__(self,name):
self.name = name
>>> class Runable(object):
pass
>>> class Flyable(object):
pass
>>> class Dog(Animal,Runable):
def __init__(self,name):
super(Dog,self).__init__(name)
>>> class Bird(Animal,Flyable):
def __init__(self,name):
super(Bird,self).__init__(name)
>>> d = Dog('wangcai')
>>> b = Bird('yingying')
聲明了Animal類和Runable類,Flyable類。子類Dog因為即是動物,又具有run的能力。是以繼承Animal類和Runable類。子類Bird因為即是動物,又具有fly的能力。是以繼承Animal類和Runable類。
繼承進階
對于python語言來講,繼承可以分為單繼承,多層繼承,多重繼承。
對于繼承來講,子類如果有構造函數__init__,不會自動調用父類的構造函數。如果子類沒有自己的構造函數__init__,則會直接從父類繼承構造函數.
>>> class Person(object):
def __init__(self):
print('Person class initing!')
>>> class Student(Person):
def __init__(self):
print('Student class initing!')
>>> s = Student()
Student class initing!//子類執行個體化對象s,并不會自動調用父類的__init__。(一定差別于C++,JAVA,C#一些面向對象語言)
如果一定需要用到父類的構造函數,則需要在子類的構造函數中顯式的調用。
>>> class Student(Person):
def __init__(self):
super(Student,self).__init__()
//調用了父類的__init__
//或者也可以寫成 Person.__init__(self)
print('Student class initing!')
>>> s = Student()
Person class initing!
Student class initing!
說明:super(type, obj),其中obj必須是type類型或者type子類類型的執行個體,否則會報錯:
TypeError: super(type, obj): obj must be an instance or subtype of type
針對多層繼承來講,super的用法也是一樣的。但是需要注意以下情況:
>>> class Person(object):
def __init__(self):
print('Person class initing!')
>>> class Man(Person):
def __init__(self):
super(Man,self).__init__()
print('Man class initing!')
>>> class Teenager(Man):
def __init__(self):
super(Teenager,self).__init__()
print('Teenager class initing!')
>>> class Student(Teenager):
def __init__(self):
super(Student,self).__init__()
print('Student class initing!')
>>> s = Student()
Person class initing!
Man class initing!
Teenager class initing!
Student class initing!
如果Student類,super(Student,self)改為super(Man,self)會有什麼結果輸出?
>>> class Person(object):
def __init__(self):
print('Person class initing!')
>>> class Man(Person):
def __init__(self):
super(Man,self).__init__()
print('Man class initing!')
>>> class Teenager(Man):
def __init__(self):
super(Teenager,self).__init__()
print('Teenager class initing!')
>>> class Student(Teenager):
def __init__(self):
super(Man,self).__init__()
print('Student class initing!')
>>> s = Student()
Person class initing!
Student class initing!
class Student(Teenager):
def __init__(self):
super(Teenager,self).__init__()
print('Student class initing!')
>>> s = Student()
Person class initing!
Man class initing!
Student class initing!
可看出super(type[,type2_or_obj]),type決定了super調用方法所在的父類--type的父類(如果有的話),即type決定了前往哪個父類調用指定的方法。
那麼super(Man,self)指的是 調用Man類父類的Person類的__init__方法。
剛才在介紹繼承的時候,說過如果子類并沒有自己的__init__方法,則會繼承父類的__init__。那麼如果是多重繼承的話,子類繼承了兩個甚至更多的類,那麼子類是繼承哪個類的__init__方法?
>>> class FatherA(object):
def __init__(self):
print('FatherA class initing!')
>>> class FatherB(object):
def __init__(self):
print('FatherB class initing!')
>>> class Son(FatherA,FatherB):
pass
>>> s = Son()
FatherA class initing!
将class Son(FatherA,FatherB)改為class Son(FatherB,FatherA)會有什麼結果?
>>> class Son(FatherB,FatherA):
pass
>>> s = Son()
FatherB class initing!
大家可以通過上面的例子,可以發現:
子類從多個父類派生,子類沒有自己的構造函數時,
(1)按繼承順序,第一個父類而它又有自己的構造函數,就繼承它的構造函數;
(2)如果最前面第一個父類沒有構造函數,則繼承第2個的構造函數,如果第2個類也沒有,則繼承第3個的。以此類推,最後會繼承object。
針對于構造函數__init__,遵循上面的繼承規則。那麼執行個體方法是否也遵循上面的繼承規則?
>>> class FatherA(object):
def __init__(self):
print('FatherA class initing!')
def ft(self):
print('FatherA ft fun!')
>>> class FatherB(object):
def __init__(self):
print('FatherB class initing!')
def ft(self,args):
print('FatherB ft fun!')
>>> class Son(FatherA,FatherB):
def __init__(self):
super(Son,self).ft()
>>> s = Son()
FatherA ft fun!
看起來執行個體方法也是遵循上面的規則,按繼承順序調用父類方法。
如果就是需要調用兩個父類的ft方法怎麼辦?
>>> class Son(FatherA,FatherB):
def __init__(self):
FatherA.ft(self)
FatherB.ft(self,0)
//顯式調用
>>> s =Son()
FatherA ft fun!
FatherB ft fun!
熟能生巧,來看看這個例子,能輸出什麼結果?
>>> class FatherA(object):
def __init__(self):
print('FatherA class initing!')
self.name = 'FatherA name'
def get_name(self):
return 'FatherA call '+ self.name
>>> class FatherB(object):
def __init__(self):
print('FatherB class initing!')
self.name = 'FatherB name'
def get_name(self):
return 'FatherB call '+ self.name
>>> class Son(FatherA,FatherB):
def __init__(self):
FatherA.__init__(self)
FatherB.__init__(self)
print('Son class initing!')
>>> s = Son()
>>> s.get_name()
輸出結果為:
>>> s = Son()
FatherA class initing!
FatherB class initing!
Son class initing!
>>> s.get_name()
'FatherA call FatherB name'
解析:
(1)在Son類中,執行__init__函數,調用了FatherA.__init__(self),FatherB.__init__(self),是以self.name 最後為FatherB name。
(2)調用了s.get_name()方法,根據python多重繼承規則,從左到右的繼承順序,調用的是FatherA的get_name方法。
繼承經典類和新式類
何為經典類/新式類?
答:經典類是python2.2之前的東西,但是在2.7還在相容,但是在3之後的版本就隻承認新式類。新式類在python2.2之後的版本中都可以使用。
經典類/新式類差別?
答:經典類是預設沒有派生自某個基類,而新式類是預設派生自object這個基類。
//經典類
class A():
pass
//新式類
class A(object):
pass
針對于經典類的多重繼承采用的是深度優先繼承.見例子:
>>> class A():
def f1(self):
print('A f1')
>>> class B(A):
def f2(self):
print('B f2')
>>> class C(A):
def f1(self):
print('C f1')
>>> class D(B,C):
pass
>>> d = D()
>>> d.f1()
A f1
解析:在通路d.f1()的時候,D這個類是沒有f1方法。那麼往上查找,先找到B,裡面也沒有,深度優先,通路A,找到了f1(),是以這時候調用的是A的f1(),進而導緻C重寫的f1()被繞過.
經典類在python3.x徹底被抛棄,在這裡就不做過多的介紹。大家記得就好。上面的執行順序:D->B->A
再來看看新式類:
>>> class A(object):
def f1(self):
print('A-f1')
>>> class B(object):
def f1(self):
print('B-f1')
>>> class A(object):
def f1(self):
print('A-f1')
>>> class B(object):
def f1(self):
print('B-f1')
def bar(self):
print('B-bar')
>>> class C1(A,B):
pass
>>> class C2(A,B):
def bar(self):
print 'C2-bar'
>>> class D(C1,C2):
pass
>>> d = D()
>>> d.f1()
A-f1
>>> d.bar()
C2-bar
從上面新式類的輸出結果來看,新式類的搜尋方式是采用“廣度優先”的方式去查找屬性。
執行個體d調用f1()時,搜尋順序是 D -> C1 -> C2 -> A
執行個體d調用bar()時,搜尋順序是 D -> C1 -> C2
歸總python繼承的特性:
1.子類如果有構造函數__init__,不會自動調用父類的構造函數。如果子類沒有自己的構造函數__init__,則會直接從父類繼承構造函數.如果一定需要用到父類的構造函數,則需要在子類的構造函數中顯式的調用.
2.子類從多個父類派生,子類沒有自己的構造函數時,
(1)按繼承順序,從左到右。第一個父類而它又有自己的構造函數,就繼承它的構造函數;
(2)如果最前面第一個父類沒有構造函數,則繼承第2個的構造函數,如果第2個類也沒有,則繼承第3個的。以此類推,最後會繼承object。
3.新式類通過廣度優先的方式查找屬性。