继承和派生

1. 继承

1. 什么是继承

  • 继承:就是新建类的方法,新建的类称之为子类或者派生类,子类继承的类叫做父类,也称之为基类或超类。简单的说:一个类可以得到另一个类名称空间中的属性和方法的这个特性,这种特性就是通过继承来实现的

  • 继承的特征:

    子类可以继承父类的属性(特征和技能),并且可以派生出自己的属性(特征和技能)

  • 在python中,一个子类可以继承多个父类,其它编程语言只能一个子类继承一个父类(面试可能会问)

2. 为什么要继承

  • 继承的目的就是为了减少代码冗余(减少重复代码)

3. 怎么继承,如何实现继承

  1. 首先要确定好谁是子类谁是父类

  2. 在定义类时,子类 + () ,()内写父类的名字,实现子类继承父类

    #  子类B继承父类A
    class A:
        pass
    class B(A):
        pass
    
    print(B.__bases__) #查看B继承的父类们,以元组飞形式打印出各父类
    print(B.__bases__[0].__dict__) # 查看B的第一个父类的属性
    
  3. 什么是抽象?
    抽象指的是抽取相似的部分,称之为抽象.

  4. 继承的关系:
    对象是特征与技能的结合体.
    类是一系列对象相同的特征与技能的结合体.
    继承是一系列类相同的特征与技能的结合体.

4. 继承后,对象的属性的搜索顺序

  • 下面的查找中,只要找到了就不会继续找了
  1. 先从对象的名称空间中查找该属性
  2. 若对象的名称空间中没有,就会去子类的名称空间中查找
  3. 若子类中没有,就去父类中查找,并且按照父类的继承顺序查找。

5. 新式类和经典类(面试会问)

  • python2 中才会有新式类和经典类之分。
  • python3 中所有的类都是新式类

1. 新式类

  • 什么是新式类:

    继承object的类都称之为新式类

    python3 中,子类如果不继承自定义的类,会默认继承object

2. 经典类

  • 什么是经典类

    在python2 中,凡是没有继承object 的类都是经典类

3. mro() / __class__ / __name__ 方法

  • mro()方法是属于object类中的方法,在多继承情况下,可以用来查看当前类的继承顺序。他返回的结果是一个元组。注意要索引取值。
  • __class__ 是对象的方法,可以获得实例化该对象的类。
  • __name__ 一种通用的方法,类/函数 通过.__name__可以获取类/函数的 名字。

4. 钻石继承(也称为菱形继承)

  • 在多继承的情况下会形成钻石继承

  • 钻石继承的继承顺序:分为 新式类 和 经典类 的继承顺序

(1) 新式类的钻石继承顺序

  • 新式类的钻石继承顺序为 : 广度优先。

  • 它的特点是按一个子类的继承时括号内的书写顺序,从左到右依次,(如子类1(A,D,G...)),先继承:

    第一个父类A——》A的父类B——》B的父类C——》。。。

    第二个父类D——》D的父类E——》E的父类F——》。。。

    。。。

    但最后继承顶层的父类,再继承object类

(2) 经典类的钻石继承顺序

  • 经典类的钻石继承顺序为: 深度优先

  • 它的特点是按一个子类的继承时括号内的书写顺序,从左到右依次,(如子类1(A,D,G...)),先继承:

    第一个父类A——》A的父类B——》B的父类C——》。。。——》顶层的父类

    第二个父类D——》D的父类E——》E的父类F——》。。。——》顶层父类继承过了,到顶层的父类的最近的一个儿子类就结束继承。开始第三个父类,第三个父类也是同第二个父类一样的继承顺序。

    。。。

2. 继承的实例

  • 通过继承实现修改json模块的数据类型

    import json
    from datetime import date, datetime
    
    print(json.JSONEncoder)
    print(datetime.today())  # 当前时间
    print(date.today())  # 当前日期
    
    # 开发者的角度: 直接转成str
    # dict1 = {
    #     'name': 'tank',
    #     'today': str(datetime.today()),
    #     'today2': str(date.today())
    # }
    #
    # res = json.dumps(dict1)
    # print(res)
    
    
    # isinstance:
    # python内置的函数,可以传两个参数,判断参数一是否式参数二的一个实例.
    
    *********************继承的使用***********************
    # 开源者的角度: 修改json源码
    class MyJson(json.JSONEncoder):
        def default(self, o):
    
            # 子类派生的功能
            # 判断o是否式datetime的一个实例
            if isinstance(o, datetime):
                return o.strftime('%Y-%m-%d %X')
            elif isinstance(o, date):
                return o.strftime('%Y-%m-%d')
            else:
                # 继承父类的default方法的功能
                return super().default(self, o)
    
    
    dict1 = {
        'name': 'tank',
        'today': datetime.today(),
        'today2': date.today()
    }
    
    res = json.dumps(dict1, cls=MyJson)  # cls=None,默认指向的是原json的JSONEncoder
    print(res)
    

3. 派生

1. 什么是派生

  • 派生指的是子类继承父类的属性,并且产生自己新的属性
  • 子类派生出新的属性,若与父类的属性相同,则以子类的为准。

2. 子类派生出新的属性,并重用父类的属性

问题:子类继承了父类的__init__属性,但还需要添加自己的一些新的属性

解决方法:

注意下面的两种方法不要混合使用

  • 方法一:

    直接通过 父类.__init__(self,要重用的属性) 来实现重用父类属性
    
    class OldboyPeople:
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    class OldboyTeacher(OldboyPeople):
        # 等级, 薪资
        def __init__(self, name, age, sex, level, sal):
            OldboyPeople.__init__(self, name, age, sex)
            self.level = level
            self.sal = sal
    
  • 方法二:(比方法一,少写了self)

    super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,
            通过"."指向的是父类的名称空间.
        
    class OldboyPeople:
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    class OldboyTeacher(OldboyPeople):
        # 等级, 薪资
        def __init__(self, name, age, sex, level, sal):
            super().__init__(name, age, sex)
            self.level = level
            self.sal = sal
    
原文地址:https://www.cnblogs.com/Mcoming/p/11648315.html