面向對象編程的一個顯著優勢就是代碼復用,繼承就是實現代碼復用的一種方式。所謂的繼承是指創建一個類時,並不是從零開始構建,而是在一個已有類的基礎上進行擴展,可以大大降低工作量。例如:編寫測試用例繼承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;經典類繼承方式為深度優先。