天天看點

Python類的繼承,你了解多少?

作者:千鋒IT教育

“三人行必有我師焉!”、“不恥下問”,中國的聖人先師孔子留下的文化瑰寶傳承在生活中的每個角落。

孔子是中國古代最偉大的思想家、教育家。如果說中國有一種根本的立國精神,能夠曆久不變,能夠浸潤于全民族的生命之中,又能夠表現中華民族獨特的倫理價值的話,無疑是孔子開創的儒家思想。

Python類的繼承,你了解多少?

這就是文化的傳承。我們Python程式設計也有這種傳承即繼承。

繼承

面向對象三大特征:封裝、繼承、多态

面向對象程式設計 (OOP) 語言的一個主要功能就是“繼承”,所謂繼承就是使現有的類無需編碼便可以擁有原有類的方法和屬性。

被繼承的類可以稱之為父類、基類、超類。繼承的類可以稱之為子類、派生類。派生和繼承是一體兩面,從父類向子類看就是派生,從子類向父類看就是繼承。子類和父類的關系可以用“is a”類表示,即子類是父類的一種,是一個更具體、更加強大的父類。python支援單繼承和多繼承。

Python類的繼承,你了解多少?

上圖中我們把【動物】看成父類而【人】和【豬】就是它的子類。或者人類也可以有子類。

Python類的繼承,你了解多少?

單繼承

在開始給大家介紹程式設計之前,首先給需要大家了解,在Python中所有的類預設繼承object。也就是說如果你定義一個Person類,則預設會繼承object。

繼承的格式:

class 父類名: # 預設繼承object,但是都是省略了object

......

class 子類名(父類名):

....

# 沒有繼承的時候
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18

    def eat(self):
        print(self.name + "正在吃飯...")
 
class Student:
    def __init__(self, name):
        self.name = name
        self.age = 18

    def eat(self):
        print(self.name + "正在吃飯...")
 
    def study(self):
      print('我要好好學習!')

class Programer:
    def __init__(self, name):
        self.name = name
        self.age = 18

    def eat(self):
        print(self.name + "正在吃飯...")
 
    def program(self):
      print('編寫程式中...')           

但是我們發現三個類中有很多相同的代碼,這時候就成了代碼備援。此時我們就需要通過繼承解決問題。

将每個類中備援的代碼提取到父類中,然後子類繼承父類的就可以了。

我們按照上面繼承的格式“改裝”代碼如下:

class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18

    def eat(self):
        print(self.name + "正在吃飯...")
 
class Student(Person):        
    def study(self):
      print('我要好好學習!')

class Programer:
    def program(self):
      print('編寫程式中...')           

此時代碼是不是就變得很簡練了,可以建立一個Student對象或者Programer對象,調用一下eat方法看看是否有列印。

構造方法的繼承

我們在上面的代碼基礎上,建立一個Student對象。

s = Student('大寶')
s.eat()
s.study()           

但是此時我們想在學生對象建立的時候就初始化一個學生的班級,我們如何實作呢?

父類__init__調用方式:

  1. super(目前類名,self)._init_(實參清單)
  2. super()._init_(實參清單)
  3. 父類名._init_(self,其它參數)
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18

    def eat(self):
        print(self.name + "正在吃飯...")

class Student(Person):  
    def __init__(self,name,clazz):
      # 調用父類的構造方法(3種實作方式)
        # super(Student,self).__init__(name)
        # super().__init__(name)
        Person.__init__(self,name)
        self.clazz = clazz
 
    def study(self):
        print(f'我在{self.clazz},我要好好學習!')


s = Student('大寶','一年級3班')
print(s)
s.eat()
s.study()
print(s.age)           

方法的重寫

有的時候從父類繼承的方法在子類往往不能滿足需求,則需要在自己的類中定義一個同名的方法,那這種操作我們稱作重寫。

比如父類Person中的eat方法不能滿足子類Student的需求了,此時就需要在Student中重寫此方法,代碼如下:

class Student(Person):  
    def __init__(self,name,clazz):
        super().__init__(name)
        self.clazz = clazz
 
    def study(self):
        print(f'我在{self.clazz},我要好好學習!')
 
    # 重寫eat方法
    def eat(self,food):
     # 此時可以調用父類原有的方法通過關鍵字super,然後再添加自己的代碼
     super().eat()
     print(f'{self.name}最喜歡的食物是:{food}')

# 建立對象
s = Student('大寶','一年級3班')
s.eat()           

結果:

大寶正在吃飯...

大寶最喜歡的食物是:漢堡

大家發現列印結果是重寫後的eat方法,是以大家記住一點:對象在調用的時候先判斷目前類是否存在此方法,如果存在調用自己的,不存在去父類找,如果父類都沒有則報錯。

繼承注意事項:

  • 并不是所有的都可以繼承哦!私有的是繼承不了的。即父類的私有屬性和私有方法是無法繼承的。
  • Python中的**super()**方法設計目的是用來解決多繼承時父類的查找問題,是以在單繼承中用不用 super 都沒關系;但是,使用 super() 是一個好的習慣。一般我們在子類中需要調用父類的方法時才會這麼用。
  • super()的好處就是可以避免直接使用父類的名字.主要用于多重繼承
class A:
    def m(self):
     print('A')
 
   class B:
    def m(self):
     print('B')
 
   class C(A):
    def m(self):
     print('C')
     super().m()
 
   c = C()
   c.m()           

這樣做的好處就是:如果你要改變子類繼承的父類(由A改為B),你隻需要修改一行代碼(class C(A): -> class C(B))即可,而不需要在class C的大量代碼中去查找、修改基類名,另外一方面代碼的可移植性和重用性也更高。

多繼承

所謂多繼承就是一個子類可以繼承多個父類。格式:

class 父類A:

......

class 父類B:

......

class 子類名(父類A,父類B,..): # 即可以通過逗号分隔跟多個父類

....

class A:
    def m(self):
        print('A')
 
class B:
    def m(self):
        print('B')
 
class C:
    def print_c(self):
        print('CCC')
 
class D(A,B,C):
    def m(self):
        print('D')
        super().m()
 
d = D()
d.m()
d.print_c()
結果:
D
A
CCC           

當對象d調用m()函數的時候,為什麼?因為首先在目前類搜尋是否存在m函數,如果存在則列印結果,不存在則去父類找。那super().m()調用的時候搜尋父類的順序是什麼呢?是按照繼承時括号裡面父類的順序依次查找是否存在,是以先判斷A類是否有m函數,有則調用,沒有繼續向下查找。

當然也可以通過調用:類名.__mro__來檢視查找m的順序。

print(D.__mro__)           

結果:

(<class 'main.D'>, <class 'main.A'>, <class 'main.B'>, <class 'main.C'>, <class 'object'>)           

但是如果我們把代碼改成如下:

class A:
    def m(self):
        print('A')
 
class B:
    def m(self):
        print('B')
 
class C(A,B):
    def print_c(self):
        print('CCC')

class D:
    def m(self):
        print('D')    
 
class E(C,D):
    def m(self):
        print(E)
        super().m()

print(E.__mro__)           

列印結果是什麼呢?自己敲代碼分析實作一下。