面向对象编程的一个显著优势就是代码复用,继承就是实现代码复用的一种方式。所谓的继承是指创建一个类时,并不是从零开始构建,而是在一个已有类的基础上进行扩展,可以大大降低工作量。例如:编写测试用例继承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单继承
classcartInfoTest(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")deftest_cart(self,):"""测试name_function.py"""rootURL=cr.INTERFACE_CARTINFOheaders=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的方法:assertEqualself.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多继承
classFather:defhobby(self):print(f"{Father.__name__}lovetoplayvideogame.")defcook(self):print(f"{Father.__name__}lovetocookanything.")classMother:defcook(self):print(f"{Mother.__name__}lovetocookanything.")defhobby(self):print(f"{Mother.__name__}lovetoplayvideogame.")classSon(Father,Mother):passif__name__=='__main__':son=Son()son.cook()son.hobby()#执行结果:Fatherlovetocookanything.Fatherlovetoplayvideogame.#更换下继承顺序classSon(Mother,Father):passif__name__=='__main__':son=Son()son.cook()son.hobby()#执行结果Motherlovetocookanything.Motherlovetoplayvideogame.
父类继承顺序:使用类的实例对象调用一个方法时,若子类未找到,则会从左到右查找父类是否包含该方法。
3.继承父类构造函数
父类定义了__init__方法,子类必须显式调用父类的__init__方法。
如果父类有init方法,子类没有,则子类默认继承父类的init方法;如果父类有init方法,子类也有,可理解为子类重写了父类的init方法。为了能使用或者扩展父类的行为,更常见的做法是在重写init方法的同时,显示调用父类的init方法,具体继承方式如下:
1.经典类的写法:父类名称.__init__(self,参数1,参数2,...)
2.新式类的写法:super(子类,self).__init__(参数1,参数2,....)
classCheckPoint(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=methodNameself._flag=0self.msg=[]#基本的布尔断言:要么正确,要么错误的验证defcheckAssertEqual(self,arg1,arg2,msg=None):"""验证arg1=arg2,不等则fail"""try:self.assertEqual(arg1,arg2,msg)exceptExceptionase:self._flag+=1self.msg.append("{}".format(msg))print(e)
如果我们只是简单地在子类Chinese中定义一个构造函数,其实就是在重写父类构造函数,这样子类就不能继承父类的属性了。所以我们在定义子类的构造函数时,要先继承再构造,这样我们也能获取父类的属性了。
子类构造函数基础父类构造函数过程如下:
实例化对象c---->c调用子类__init__()---->子类__init__()继承父类__init__()----->调用父类__init__()
继承父类构造函数的三种方法:
#方法一:经典类写法,需要把父类的拿过来,把父类执行一遍:父类名.__init__(self,父类参数)#方法二:super(子类名,self).__init__(methodName)#第三种写法(推荐):Python3的写法,与第二种等价:super().__init__(methodName)
扩展知识点:
定义类时,如果没有指定父类,则默认的父类为object,object类是所有类的直接父类或间接祖先类。
class类名与class类(object)并不只是是否写继承object父类的区别,实际他们是两个概念,class类:是经典类,class类(object)是新式类,新式类是经典类的升级,类似于Python2与Python3的区别。
在Python2及以前的版本,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于新式类;反之,不由任意内置类型派生出的类,则称之为经典类
在Python3以后,没有该区分,所有的类都派生自内置类型object,不管有没有显式继承object,都属于新式类。
经典类和新式类区别主要在于多继承上的顺序问题,新式类继承方式为广度优先,横向所有策略查完,再去向上查A;经典类继承方式为深度优先。