面向对象编程的一个显著优势就是代码复用,继承就是实现代码复用的一种方式。所谓的继承是指创建一个类时,并不是从零开始构建,而是在一个已有类的基础上进行扩展,可以大大降低工作量。例如:编写测试用例继承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)
扩展知识点:
定义类时,如果没有指定父类,则默认的父类为object,object类是所有类的直接父类或间接祖先类。
class 类名与class 类(object)并不只是是否写继承object父类的区别,实际他们是两个概念,class 类:是经典类,class 类(object)是新式类,新式类是经典类的升级,类似于Python2与Python3的区别。
在Python2及以前的版本,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于新式类;反之,不由任意内置类型派生出的类,则称之为经典类
在Python3以后,没有该区分,所有的类都派生自内置类型object,不管有没有显式继承object,都属于新式类。
经典类和新式类区别主要在于多继承上的顺序问题,新式类继承方式为广度优先,横向所有策略查完,再去向上查A;经典类继承方式为深度优先。