這一章節主要講解面向對象高級編程->繼承進階篇,包括類多繼承介紹和繼承經典類和新式類屬性的查找順序不一樣之處。python
上一章節咱們講到繼承,子類繼承父類,能夠擁有父類的屬性和方法,也能夠進行擴展。可是有時候會發現,子類須要繼承一個類的方法,又要繼承另外一個類的方法,才能完成要實現的功能。怎麼辦?python給咱們提供了多繼承的概念。相似於C++語言,俗稱類的多繼承。編程
看個例子:python3.x
>>> 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語言來說,繼承能夠分爲單繼承,多層繼承,多重繼承。spa
對於繼承來說,子類若是有構造函數__init__,不會自動調用父類的構造函數。若是子類沒有本身的構造函數__init__,則會直接從父類繼承構造函數.code
>>> 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子類類型的實例,不然會報錯:blog
TypeError: super(type, obj): obj must be an instance or subtype of type繼承
針對多層繼承來說,super的用法也是同樣的。可是須要注意如下狀況:get
>>> 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.新式類經過廣度優先的方式查找屬性。