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;经典类继承方式为深度优先。
posted @ 2021-05-22 23:23  不吃鱼的猫大  阅读(4105)  评论(0编辑  收藏  举报