继承 inheritance 和 派生  derived
    什么是继承 / 派生
        1.继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的行为
        2.派生类就是从一个已有的类中衍生出新的类,在新类的基础上添加新的属性和行为

    为什么继承 / 派生
        继承的目的是延续旧类的功能
        派生的目的是在旧类的基础上改变原有的功能
    名词:
        基类(base class)/超类(super class)/父类(father class)
        派生类(derived class)/子类(child class)

单继承
    语法:
        class 类名(基类名):
            语句块
    说明:
        单继承是指派生类由一个基类衍生出来新类
    示例:
 

#此示例示意单继承的定义方法和用法
class Human:
    def say(self,what):
        print('说',what)
    def walk(self,distance):
        print('走了',distance,'公里')
 
class Student(Human):
    def study(self,subject):
        print('正在学习',subject)
 
class Teacher(Student):
    def teach(self,what):
        print('说',what)
 
 
h1 = Human()
h1.say('今天天气真好') #说 今天天气真好
h1.walk(5)  #走了 5 公里
 
s1 = Student()
s1.walk(4)  #走了 4 公里
s1.say('感觉有点累') #说 感觉有点累
s1.study('python') #正在学习 python
 
t1 = Teacher() 
t1.teach('面向对象') #说 面向对象
t1.walk(6)  #走了 6 公里
t1.say('太累了,今天吃麻辣香锅') #说 太累了,今天吃麻辣香锅
t1.study('英雄联盟  ')  #正在学习 英雄联盟

多继承 multipe inhereitance

        多继承是指一个子类继承来自两个或两个以上的基类

        语法:
            class 类名(基类名1,基类名2,.......)
        说明:
            一个子类同时继承自多个父类,父类中的方法可以同时被继承下来
            如果两个父类中有同名的方法,而在子类中又没有付给此方法时,调用结果难以确定
        示例:

#multiple_inherit.py
 
class Car:
    def run(self,speed):
        print('汽车以',speed,'公里/小时的速度行驶')
 
class Plane:
    '''飞机类'''
    def fly(self,height):
        print('飞机以海拔',height,'米的高度飞行')
 
class PlaneCar(Car,Plane):
    '''PlaneCar类同时继承自汽车类和飞机类'''
 
p = PlaneCar() #创建一个飞行汽车对象
p.fly(110000)
p.run(100)

多继承的问题(缺陷)
            标识符冲突问题
            (要谨慎使用多继承)

            示例:

#multiple_inherit2.py
 
小张写了一个类A
class A:
    def m(self):
        print('A.m()被调用')
 
#小李写了一个类 B
class B:
    def m(self):
        print("B.m()被调用")
 
 
小王感觉小李和小张写的两个类自己都能用
 
class AB(B,A):
    pass
 
ab = AB()
ab.m()

多继承的MRO(Method Resolution Order)

            python3的类的__mro__属性
            作用:
                用来记录类的方法查找顺序

            示例:

# mro.py
 
class A:   #4
    def go(self):
        print('A')
 
class B(A):  #2
    def go(self):
        print('B')
        super().go()  #C
        
class C(A): #3
    def go(self):
        print('C')
 
class D(B,C):  #1
    def go(self):
        print('D')
        super().go()
d = D()
d.go()

继承派生机制的作用
    1.可以将一些共用的功能加在基类中,实现代码的共享 
    2.在不改变基类的基础上改变原有的功能

练习:
    list类里只有append向末尾加一个元素的方法,但没有向列表头部添加元素的方法
    试想能否为列表在不改变原有功能的基础上添加一个inster_head(x)方法,此方法能在列表的前部添加元素
 

class Mylist(list):
    def insert_head(self,x):
        # self.reverse()
        # self.append(x)
        # self.reverse()
        self.insert(0,x)  #直接在最开始插入x
 
 
 
myl = Mylist(range(3,6))
print(myl)      #[3.4.5]
myl.insert_head(2)
print(myl)      #[2,3,4,5]
myl.append(6)
print(myl)      #[2,3,4,5,6]

继承说明:
    Python 3任何类型都直接或间接的继承object类
    object类是一切类的超类

类的__base__属性:
    __base__属性用来记录此类的基类

python内建的类详见:
    >>>help(__builtins__)

覆盖 override
    什么是覆盖
        覆盖是指在继承关系的类中,子类中实现了与基类同名的方法,
        在子类的实例调用该方法时,实际调用的是子类的覆盖版本,这种现象叫做覆盖

    示例:
 

#此示例示意覆盖的含义及方法
class A:
    def work(self):
        print('A.work被调用')
class B(A):
    '''B类继承子A类'''
    def work(self):
        print('B.work被调用')
 
b = B()
b.work()    #B.work被调用
 
a = A()
a.work()    #A.work被调用
# A.work(b)   #A.work被调用

问题:
    当覆盖发生时,子类对象如何调用父类中的被覆盖方法

调用方式:
    基类名.方法名(实例,实际调用传参.....)

super函数
    super(cls,obj)返回被绑定超类的实例(要求obj必须为cls类型的实例)
    super() 返回被绑定超类的实例,等同于:super(__class__,实例方法的第一个参数,必须在方法内调用)

    作用:
        借助super()返回的实例间接调用父类的覆盖方法
    示例;
 

>>> #此示例示意用super函数间接调用父类的
class A:
    def work(self):
        print('A.work被调用')

        
>>> class B(A):
    '''B类继承子A类'''
    def work(self):
        print('B.work被调用')
 
    def super_work(self):
        #调用b类自己的work方法
        self.work()
        #调用父类的work
        super(B,self).work()
        super().work()  #此种调用方式只能在实例方法内调用

        
>>> b = B()
>>> b.work()
B.work被调用
>>> super(B,b).work()
A.work被调用
>>> b.super_work()
B.work被调用
A.work被调用
A.work被调用
>>>

显示调用基类的初始化方法
    当子类中实现了__init__方法,基类的构造方法并不会被调用,此时需要显示调用
    示例:

>>> #此示例示意显示调用初始化方法
>>> class Human:
    def __init__(self,n,a):
        self.name = n
        self.age = a
        print("Human类的初始化方法被调用...")
 
    def infos(self):
        print('姓名:',self.name)
        print('年龄:',self.age)

        
>>> class Student(Human):
    def __init__(self,n,a,s = 0):
        super(Student,self).__init__(n,a)
        self.score = s
        print("Student的初始化方法被调用...")
    def infos(self):
        super().infos()  #显示调用父类的方法
        # print('姓名',self.name)
        print("成绩",self.score)

        
>>> s1 = Student('张飞', 15, 80)
Human类的初始化方法被调用...
Student的初始化方法被调用...
>>> s1.infos()
姓名: 张飞
年龄: 15
成绩 80
>>>

调用基类初始化方法的另一种方法:

class Student(Human):
    def __init__(self,n,a,s = 0):
        Human.__init__(self,n,a)
        self.score = s
        print("Student的初始化方法被调用...")
    def infos(self):
        super().infos()  #显示调用父类的方法
        print("成绩",self.score)

        
>>> s1 = Student('张飞', 15, 80)
Human类的初始化方法被调用...
Student的初始化方法被调用...
>>> s1.infos()
姓名: 张飞
年龄: 15
成绩 80
>>>