Python類的繼承


    面向對象編程的一個顯著優勢就是代碼復用,繼承就是實現代碼復用的一種方式。所謂的繼承是指創建一個類時,並不是從零開始構建,而是在一個已有類的基礎上進行擴展,可以大大降低工作量。例如:編寫測試用例繼承unittest.TestCase父類

1. 繼承與被繼承概念

  在Python中,新建的類可以繼承一個或多個父類,通過繼承創建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。子類可以繼承父類的公有屬性/方法,但不能繼承其私有屬性/方法。如果需要在子類中調用父類的方法,可以使用“super().方法名()” 或者通過“基類名.方法名()”的方式來實現。
  類的繼承語法如下:
class 子類名(父類1, 父類2, ...., 父類n):
    pass
    
# 通過類提供的__bases__屬性可以查看到子類直接繼承的所有父類,子類名.__bases__
  如果在類定義中沒有指定父類,則默認父類繼承object,object是所有類的根基類,此時可以省去類名后面的圓括號。object類中定義的所有方法名稱都是以兩個下划線開始,以兩個下划線結束,其中比較重要的方法有__ne__()、__init__()、__str__()、__eq__()和__dir__()

2. 繼承方式

  在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關系應該是“屬於”關系。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Cat類卻不能繼承 Person 類,因為貓並不是一個人。
2.1單繼承
class cartInfoTest(unittest.TestCase):   # 定義一個子類cartInfoTest, 繼承父類unittest.TestCase類
    # 購物車的測試用例
    storeID = [1, '北京XX', 11, 'XX店', '116.316839', '39.982926', '6a84ee3d9eb72755500fe083956628d1@MS0xMTItMQ', '00dfb855c6465c81199a21672fca1f18@MTM3Mi02OTYzMg', '2']
    userInfo = ("0f776f21-4455-4342-852c-ba8d802338d3", "60E7BD2AF307BCBA39926EC51D165429CF17DDE262FAA204E701D2BAE7DF1BEF0B9AFC57D96EDE299471E2BD9F905CDE081D053C1FEF126BA9C0173F585F0B3469B30A2965B1651600679E68E610E197EF72EAFEC66D6184F1022DB5FE38B223768D1D7D029616164F05DA3128473184212D42152629DB9E3E1C800A5AD65469")
    def test_cart(self,):
        """測試name_function.py"""
        rootURL = cr.INTERFACE_CARTINFO
        headers = cr.homePageInterfaceTestHeader(self.storeID, token=self.userInfo[0], ticketName=self.userInfo[1])
        param = cr.cartInfoTestParams(self.storeID)
        data = "param=%s" % json.dumps(param, ensure_ascii=False)
        r = requests.post(rootURL, headers=headers, data=data)
        
        # 調用繼承的父類unittest.TestCase的方法:assertEqual
        self.assertEqual('0000', r.json()['code'], r.json()['result'])
        # unittest.TestCase.assertEqual(self, 1, 2)  #“基類名.方法名()”
if __name__ == '__main__':
    unittest.main()

調用父類方法方式:

  ①self.方法名(),舉例你父親的錢其實也是你的錢,是不是可以直接拿來用,這種方式比較常用
  ②父類名.方法名(), 聲明方法歸屬人,如果已繼承可直接使用。

繼承實現方法:

  讓 cartInfoTest 類繼承 unittest.TestCase類,這樣當 cartInfoTest類對象調用 assertEqual()方法時,Python 解釋器會先去 cartInfoTest中找以 assertEqual()為名的方法,如果找不到,它還會自動去 unittest.TestCase類中找。
2.2 多繼承
class Father:

    def hobby(self):
        print(f"{Father.__name__} love to play video game.")

    def cook(self):
        print(f"{Father.__name__} love to cook anything.")


class Mother:

    def cook(self):
        print(f"{Mother.__name__} love to cook anything.")

    def hobby(self):
        print(f"{Mother.__name__} love to play video game.")


class Son(Father, Mother):
    pass


if __name__ == '__main__':
    son = Son()
    son.cook()
    son.hobby()
# 執行結果:
Father love to cook anything. 
Father love to play video game.

#更換下繼承順序
class Son(Mother, Father):
    pass


if __name__ == '__main__':
    son = Son()
    son.cook()
    son.hobby()
#執行結果
Mother love to cook anything. 
Mother love to play video game.
父類繼承順序:使用類的實例對象調用一個方法時,若子類未找到,則會從左到右查找父類是否包含該方法。

3. 繼承父類構造函數

  父類定義了__init__方法,子類必須顯式調用父類的__init__方法。

  如果父類有init方法,子類沒有,則子類默認繼承父類的init方法;如果父類有init方法,子類也有,可理解為子類重寫了父類的init方法。為了能使用或者擴展父類的行為,更常見的做法是在重寫init方法的同時,顯示調用父類的init方法,具體繼承方式如下:
  1.經典類的寫法: 父類名稱.__init__(self,參數1,參數2,...)
  2. 新式類的寫法:super(子類,self).__init__(參數1,參數2,....)
class CheckPoint(unittest.TestCase):

    def __init__(self,  methodName='runTest'):
        # 方法一:經典類寫法,需要把父類的拿過來, 把父類執行一遍
        #unittest.TestCase.__init__(self, methodName)  
        """方法二:super的作用Man繼承父類的構造函數,
                        優點1:父類名稱改變后,此處不用同步修改,
                        優點2:多繼承時,不用每個都寫一遍"""
        #super(CheckPoint, self).__init__(methodName)
        #第三種寫法(推薦):Python3的寫法,與第二種等價
        super().__init__(methodName)
        self._testMethodName = methodName
        self._flag = 0
        self.msg = []

    # 基本的布爾斷言:要么正確,要么錯誤的驗證
    def checkAssertEqual(self, arg1, arg2, msg=None):
        """    驗證arg1=arg2,不等則fail"""
        try:
            self.assertEqual(arg1, arg2, msg)
        except Exception as e:
            self._flag += 1
            self.msg.append("{}".format(msg))
            print(e)
  如果我們只是簡單的在子類Chinese中定義一個構造函數,其實就是在重寫父類構造函數,這樣子類就不能繼承父類的屬性了。所以我們在定義子類的構造函數時,要先繼承再構造,這樣我們也能獲取父類的屬性了。
子類構造函數基礎父類構造函數過程如下:
實例化對象c ----> c 調用子類__init__() ---- > 子類__init__()繼承父類__init__() ----- > 調用父類 __init__()
  繼承父類構造函數的三種方法:
# 方法一:經典類寫法,需要把父類的拿過來, 把父類執行一遍:父類名.__init__(self,父類參數)  
# 方法二:super(子類名, self).__init__(methodName)
#第三種寫法(推薦):Python3的寫法,與第二種等價:super().__init__(methodName)
 super的用法的見我的另一篇文章 論super().__init__()的用法
 
 
擴展知識點:
定義類時,如果沒有指定父類,則默認的父類為object,object類是所有類的直接父類或間接祖先類。
class 類名與class 類(object)並不只是是否寫繼承object父類的區別,實際他們是兩個概念,class 類:是經典類,class 類(object)是新式類,新式類是經典類的升級,類似於Python2與Python3的區別。
Python2及以前的版本,由任意內置類型派生出的類(只要一個內置類型位於類樹的某個位置),都屬於新式類;反之,不由任意內置類型派生出的類,則稱之為經典類
在Python3以后,沒有該區分,所有的類都派生自內置類型object,不管有沒有顯式繼承object,都屬於新式類
經典類和新式類區別主要在於多繼承上的順序問題,新式類繼承方式為廣度優先,橫向所有策略查完,再去向上查A;經典類繼承方式為深度優先。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM