继承与派生

继承与派生

一、继承

  • 继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类

    ​ 在python中,一个子类可以继承多个父类

    ​ 在其他语言中,一个子类只能继承一个父类

  • 继承的作用

    • 减少代码的冗余
  • 如何实现继承

    • 先确认谁是子类,谁是父类
    • 在定义子类时,子类名(父类名)
class Father1:
    pass
class Father2:
    pass
class Father3:
    pass
# 子类
class Sub(Father1, Father2, Father3):
    pass
# 子类 .__bases__ 查看父类
print(Sub.__bases__)
print(Sub.x)

二、 如何寻找继承关系

  • 确认谁是子类
    • 对象——>子类——>父类
  • 得先抽象,再继承
    • 抽取对象之间相似的部分,总结出类
    • 抽取类之间相似的部分,总结出父类
# 问题: 代码冗余
# 老师类
class OldboyTeacher:
    school = 'oldboy'
    country = 'China'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    # 老师修改分数
    def change_score(self):
        print ('老师%s正在修改分数...' % self.name)

#学生类
class OldboyStudent:
    school = 'oldboy'
    country = 'China'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    # 学生选择课程
    def choose_course(self):
        print ('学生%s正在选择课程' % self.name)

stu1 = OldboyStudent('maomao', 18, '男')
print (stu1.school, stu1.name, stu1.age, stu1.sex)

tea1 = OldboyTeacher('tank', 28, '男')
print (tea1.school, tea1.name, tea1.age, tea1.sex)

上面的代码看起来冗余很多,所有有了以下的解决办法

# 解决代码冗余问题
# 老男孩人类
class OldboyPeople:
    school = 'oldboy'
    country = 'China'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

# 老师类
class OldboyTeacher(OldboyPeople):
    # 老师修改分数
    def change_score(self):
        print (f'老师{self.name}正在修改分数...')

# 学生类
class OldboyStudent(OldboyPeople):
    # 学生选择课程
    def choose_course(self):
        print (f'学生{self.name}正在选择课程...')

stu1 = OldboyStudent('maomao', 18, '男')
print (stu1.school, stu1.name, stu1.age, stu1.sex)
tea1 = OldboyTeacher('tank', 28, '男')
print(tea1.school, tea1.name, tea1.age, tea1.sex)

三、在继承背景下对对象属性的查找顺序

注意:程序的执行顺序是由上到下,所以父类必须定义在子类的上方

  • 在继承背景下,对象属性的查找顺序:
    • 先从对象自己的名称空间中查找
    • 对象中没有,从子类的名称空间中查找
    • 子类中没有,从父类的名称空间中查找,若父类没有,则会报错
# 父类
class Goo:
    x = 10
# 子类
class Foo(Goo):
    x = 100
foo_obj = Foo()

四、 派生

派生:指的是紫萼里继承父类的属性和方法,并且派生出自己独有的属性与方法

如果子类中的方法名与父类的相同,优先使用子类的

# 父类
class Foo:
    def f1(self):
        print ('from FOO.f1...')

    def f2(self):
        print ('from Foo.f2...')

# 子类
class Bar(Foo):
    def f1(self):
        print ('from Bar.f1...')

    def func(self):
        print ('from Bar.func...')
# 派生后继承关系查找验证
bar_obj = Bar()
bar_obj.f2()
# 结果
    from Foo.f2...
    from Bar.f1...

五、重用

  • 子类继承父类,派生出自己的属性和方法,并且重用父类的属性与方法

问题就是: 子类重写父类中的__init__导致代码更加冗余

解决问题:子类重用父类的属性,并派生出新的属性

  • 两种解决方式
    • 直接应用父类的__init__为其传参,并添加子类的属性
    • 通过super来指向父类的属性
      • super() 是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间

注意:使用哪一种都是可以的,但是不可以两种混合使用

# 方式二
class OldboyPeople:
    school = 'oldboy'
    
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
        
class OldboyTeacher(OldboyPeople):
    def __init__(self, name, age, sex, sal):
        # super()——>特殊的对象——>对象.属性——>父类的名称空间
        # 会将调用类传入的对象当作第一个参数传给__init__()
        super().__init__(name, age, sex)
        self.sal = sal
        
    def change_score(self):
        print (f'老师{self.name}正在修改分数...')
        
class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex, home):
        super().__init__(name, age, sex)
        self.home = home
        
    def choose_course(self):
        print (f'学生{self.name}正在选择课程...')
        
tea1 = OldboyTeacher('tank', 18, 'male', 20000)
print (tea1.name,tea1.age,tea1.sex,tea1.sal)
stu1 = OldboyStudent('maomao', 18, 'female', '上海市')
print (stu1.name,stu1.age,stu1.sex,stu1.home)

super的方式就可以避免很多的冗余代码,每个子类下都不需要再重新写self

六、 经典类与新式类

  • 新式类
    • 凡是继承object的类或子孙都是新式类
    • 在python3中所有的类都默认继承object
  • 经典类
    • 在python2中才会有经典类与新式类之分
    • 在python2中,凡是没有继承object的类,都是经典类
class User(object):
    pass

class User:
    x = 10
    pass

class Sub(User):
    pass

七、mro继承顺序

调用mro返回的是一个继承序列

super的继承顺序严格遵循mro继承序列

class Father1:
    x = 10
    
class Father2:
    x = 20
    # 多继承的情况下,从左到右
class Sub(Father1, Father2):
    def __init__(self):
        print (super().__delattr__)
        
print (Sub.mro())
obj = Sub()
print (object)

python3中提供了一个查找新式类查找顺序的内置方法

​ mro():会把当前类的继承关系列出来

# 注意:super()会严格按照mro列表的顺序往后查找
class A:
    def test(self):
        print ('from A test')
        super().test()

class B:
    def test(self):
        print ('from B.test')

class C(A,B):
    pass
c =C()
# 检查super的继承顺序
print (C.mro())

# 去A找,有的话打印,然后super又执行了test,根据mro中查找打印B类中的test

八、 钻石继承(菱形继承)

多继承的情况下,造成“钻石继承”

  • mro的查找顺序
    • 新式类
      • 广度优先
    • 经典类
      • 深度优先

原文地址:https://www.cnblogs.com/YGZICO/p/11959700.html