天天看點

python學習第十六天 --繼承進階篇

這一章節主要講解面向對象進階程式設計->繼承進階篇,包括類多繼承介紹和繼承經典類和新式類屬性的查找順序不同之處。

多繼承

上一章節我們講到繼承,子類繼承父類,可以擁有父類的屬性和方法,也可以進行擴充。但是有時候會發現,子類需要繼承一個類的方法,又要繼承另一個類的方法,才能完成要實作的功能。怎麼辦?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.新式類通過廣度優先的方式查找屬性。

繼續閱讀