Python面对对象:继承(详解)

一、什么是继承?

继承就是让类和类之间转变为父子关系,子类可以直接访问(调用)父类的静态属性和方法。
在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。


class ParentClass1: #定义父类1
    pass
class ParentClass2: #定义父类2
    pass
class SubClass1(ParentClass1):
    # 单继承,基类是ParentClass1,派生类是SubClass
    pass
class SubClass2(ParentClass1,ParentClass2):
    # python支持多继承,用逗号分隔开多个继承的类
    pass

print(SubClass1.__bases__)  # 查看所有继承的父类
print(SubClass2.__bases__)
# ===============
# (<class '__main__.Father1'>,)
# (<class '__main__.Father1'>, <class '__main__.Father2'>)

二、继承的规则

1、子类继承父类的成员变量和成员方法
2、子类不继承父类的构造方法,能够继承父类的析构方法
3、子类不能删除父类的成员,但可以重定义父类成员
4、子类可以增加自己的成员
注:下方代码有演示


# python中子类继承父类成员变量之间的取值逻辑

class Person():

    def __init__(self, name, age, sex):
        self.name = "jasn"
        self.age = '18'
        self.sex = sex

    def talk(self):
        print("i want to speak something to yo!!")


class Chinese(Person):
    def __init__(self, name, age, sex, language):
        Person.__init__(self, name, age, sex)  # 用父类的name,age,sex 覆盖掉子类的属性
        self.age = age  # 覆盖掉了父类的age,取值为子类实例中传入age参数
        self.language = "chinese"

    def talk(self):
        print("我说的是普通话!!")
        Person.talk(self)


obj = Chinese("nancy",'18','male',"普通话")

print(obj.name)  # 对应场景A
print(obj.age)  # 对应场景B
print(obj.language)  # 对应场景C
obj.talk()  # 对应场景D

# 总结:
# A:若父类中初始化了成员变量,子类调用父类构造方法未覆盖属性(self.name),则调用子类属性时取值为父类中初始化的成员变量;
# B:若父类中初始化了成员变量,若子类调用父类构造方法覆盖属性(self.age)则取值为子类实例中传入参数
# C:若父类未初始化该成员变量,则无论子类中有无进行对父类构造方法进行属性的覆盖,均取子类实例中传入的参数
# D:对于方法,如果子类有这个方法则直接调用,如果子类没有则去父类查找。父类没有则报错

三、 继承的作用

1、实现代码(功能)的重用,降低代码冗余
2、增强软件可扩充性
3、提高软件的维护性

四、 继承与抽象的概念

面向对象中的两个重要概念:抽象和分类。
抽象和分类是人们认识世界的基本方法:

抽象的概念图

抽象是将现实世界中客观存在的事务映射到意识中的一种方法。

继承:
分类的概念

分类是指确定这些抽象到意识中的概念之间的关系。这些关系的基本形式包括一般到特殊和整体与局部。

class animal():   # 定义父类
    country = 'china'     # 这个叫类的变量
    def __init__(self,name,age):
        self.name = name   # 这些又叫数据属性
        self.age = age

    def walk(self):         # 类的函数,方法,动态属性
        print('%s is walking'%self.name)

    def say(self):
        pass

class people(animal): # 子类继承父类
    pass
    
class pig(animal): # 子类继承父类
    pass

class dog(animal): # 子类继承父类
    pass

aobama=people('aobama',60)   # 实例化一个对象
print(aobama.name)
aobama.walk()

五、派生类

1. 在父类的基础上产生子类,产生的子类就叫做派生类
2. 父类里没有的方法,在子类中有了,这样的方法就叫做派生方法。
3. 父类里有,子类也有的方法,就叫做方法的重写(就是把父类里的方法重写了)

class Hero:
    def __init__(self, nickname,aggressivity,life_value):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
    def attack(self, enemy):
        print('Hero attack')

class Garen(Hero):
    camp = 'Demacia'
    def attack(self, enemy): #self=g1,enemy=r1
        # self.attack(enemy) #g1.attack(r1),这里相当于无限递归
        Hero.attack(self,enemy)  # 引用 父类的 attack,对象会去跑 父类的 attack
        print('from garen attack')  # 再回来这里

    def fire(self):
        print('%s is firing' % self.nickname)

class Riven(Hero):
    camp = 'Noxus'

g1 = Garen('garen', 18, 200)
r1 = Riven('rivren', 18, 200)
g1.attack(r1)
# print(g1.camp)
# print(r1.camp)
# g1.fire()

六、继承的查找顺序

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

深度查找:
深度查找

当类是经典类时候,多继承情况下,在要查找的属性不存在,会按照深度优先方式查找下去。

广度查找:
在这里插入图片描述

当类是新式类(python3中只有新式类),多继承情况下,再要查找属性不存在时,会按照广度优先的方式查找下去

''''
当类是新式类,多继承的情况下,在要查找的属性不存在时,会按照广度优先来查找
默认从左侧分支查找,如 :class G(D,E,F):会先从 D 的分支查找 找到 D-C 然后从E-B -F 顺序查找
最后再查找 几个类所共同继承的顶级父类,如果顶级没有,则报错
'''

class A:
    def test(self):
        print('A中的test')

    pass

class B(A):
     def test(self):
         print('B中的test')
    pass
class C(A):
     def test(self):
         print('C中的test')
    pass
class D(C):
     def test(self):
         print('D中的test')
    pass
class E(B):
     def test(self):
         print('E中的test')
    pass

class F(A):
     def test(self):
         print('F中的test')
    pass

class G(D,E,F):
     def test(self):
         print('G中的test')
    pass

obj=G()
obj.test()

'''
第一次 E中的test
注释D,第二次:C中的test
注释C,第三次:E中的test
注释E,第四次:B中的test
注释B,第五次,F中的test
注释F,第六次,A中的test

所以新式类的继承关系(查找顺序)为:  
obj-->G --> D-->C -->E -->B -->F -->A  
也称广度优先

'''
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类 class A(object) 为经典类

主要知识点:类的__mro__ 属性的用法

七、父类属性(方法)的重用

1、指名道姓的重用


#指名道姓的重用

class A:
    def __init__(self):
        print('A的构造方法')

class B(A):
    def __init__(self):
        print('B的构造方法')
        A.__init__(self)


class C(A):
    def __init__(self):
        print('C的构造方法')
        A.__init__(self)


class D(B,C):
    def __init__(self):
        print('D的构造方法')
        B.__init__(self)  # 先找到B,B调用A,等这个线性任务处理完之后,在继续下一行代码
        C.__init__(self)  # 先找到C,C里面也调用A的方法

    pass
f1=D()  #A.__init__被重复调用
'''
D的构造方法
B的构造方法
A的构造方法
C的构造方法
A的构造方法
'''

2、super()方法重用

#使用super()
class A:
    def __init__(self):
        print('A的构造方法')
class B(A):
    def __init__(self):
        print('B的构造方法')
        super(B,self).__init__()


class C(A):
    def __init__(self):
        print('C的构造方法')
        super(C,self).__init__()


class D(B,C):
    def __init__(self):
        print('D的构造方法')
        super().__init__()  # super(D,self).__init__()
        
f1=D() #super()会基于mro列表,往后找
'''
D的构造方法
B的构造方法
C的构造方法
A的构造方法
'''
# super() 语法
# super(type[, object-or-type])   type 当前类,object-or-type 为实例化对象,一般默认为self,不过该参数在python3中默认

八、组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合:一个对象的数据属性是另一个对象,称为组合

class Equip: #武器装备类
    def fire(self):
        print('我是暴风大剑,攻击力加999')

class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
    camp='Noxus'
    def __init__(self,nickname):
        self.nickname=nickname
        self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性

r1=Riven('亚瑟')
r1.equip.fire() # 可以使用组合的类产生的对象所持有的方法,对其他英雄造成999的伤害

# 我是暴风大剑,攻击力加999

组合和继承的区分:
1、继承是"",归属的意思,比如,人,归属于动物类
2、组合是 “” 的意思,比如学生有班级、课程、老师等,当然不同的学生有不同的老师和课程,所有不能用类来继承,这个时候就可以用组合来绑定关系

'''
类的组合,即在类实例化时,将另一个类的实例作为参数传入,这样可以将两个实例关联起来。
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。 
比如学生类和课程类,学生需要选课,可以选一门或者多门,在没选课之前,学生类和课程类无关
当类之间有很多相同的属性,提取这些统统的属性做成基类,用继承比较好。

'''

# 学校类 是学生类和老师类的父类
class School:
    schoolName = '学习训练营'

    def __init__(self, name, sex, age, birth):
        self.name = name
        self.sex = sex
        self.age = age
        self.birth = birth   # 类的组合 ,将生日在初始化的时候就被调用

# 学生类
class students(School):

    def __init__(self,name,sex,age,id, birth):
        School.__init__(self,name,sex,age, birth)
        self.id = id
        self.course = []

    def Learn(self):
        print('%s,正在学习'%self.name)

# 老师类
class teacher(School):

    def __init__(self, name, sex, age, level, birth):
        School.__init__(self, name, sex, age, birth)
        self.id = id
        self.level = level
        self.course = []

    def Teach(self):
        print('%s,正在上课' % self.name)

# 课程类 是学生和老师的组合类
class Course:
    def __init__(self, name, price, cycle):
        self.name = name
        self.price = price
        self.cycle = cycle

    def all_info(self):
        print('课程信息:%s---%s---%s'%(self.name,self.price, self.cycle))

# 生日类,是学生类和老师类的组合类
class date:
    def __init__(self,year,month,day):
        self.year = year
        self.mouth = month
        self.day = day

    def birthady(self):
        print('出生日期为:%s-%s-%s'%(self.year,self.mouth,self.day))

# 学生
# birth = date('1993', 12, 23)
# stu1 = students('jasn', 'male', 18, '002', birth)
# stu1.Learn()
# stu1.birth.birthady()

# 老师
birth = date('1993', 12, 23)  # 定义生日对象
python = Course('python', 13000, '3mon') # 定义课程对象
linux = Course('linux', 13000, '3mon')

teach1 = teacher('nancy','male',18,10, birth)
teach1.Teach()
teach1.birth.birthady()
teach1.course.append(python)  # 将课程添加到老师中,这也是一种组合关系

# 查看课程
for course in teach1.course:
    course.all_info()

九、抽象类和归一化设计

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处:
1、归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2、归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
    def read(self): #定接口函数read
        pass

    def write(self): #定义接口函数write
        pass

class Txt(Interface): #文本,具体实现read和write
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(Interface): #磁盘,具体实现read和write
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(Interface):
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

十、抽象类的用法

子类必须继承抽象类的方法,不然报错

1、什么是抽象类?

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

2、为什么要有抽象类?

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

比如您开汽车的话,你只会开奥迪、奔驰…等众多汽车品牌中的一种,而不可能开着抽象化的"车"上路。

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。

3、抽象类与接口

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

'''
我们有一个汽车参数接口,里面定义了汽车所有的必须需要的参数,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,
他们都实现了汽车接口,这样就好办了,大家选择汽车的时候,只需要将不同汽车的参数来进行对比,就能评判出一个车的好坏。
'''

import abc

# 抽象类:本质还是类,与普通类额外的特点的是:加了装饰器的函数,子类必须实现他们
class Car(metaclass=abc.ABCMeta):

    @abc.abstractmethod 
    def name(self): # 如果子类没有name 函数,主动抛出异常
        '必须定义车名字'
        pass

    @abc.abstractmethod
    def Model(self): # 如果子类没有Model 函数,主动抛出异常
        '必须定义车型号'
        pass

    @abc.abstractmethod
    def drive(self):
        ' 必须定义驱动方式 '
        pass

    @abc.abstractmethod
    def Fuel(self):
        '必须定义油耗'
        pass

    @abc.abstractmethod
    def engine(self):
        '必须定义发动机'
        pass

    @abc.abstractmethod
    def power(self):
        '必须定义功率'
        pass


class AudiCar(Car):
    name = '一汽-大众奥迪-奥迪Q5L'
    def Model(self):
       print('中型SUV')
    def drive(self):
         print('前置四驱7挡双离合')
    def Fuel(self):
         print('7.1L/100km(工信部)')
    def engine(self):
         print('2.0T 190马力 L4')
    def power(self):
         print('140kW/320N.m')

class BMWCar(Car):
    name = '宝马'
    def Model(self):
       print('中型SUV')
    def drive(self):
         print('前置四驱7挡双离合')
    def Fuel(self):
         print('5.1L/100km(工信部)')
    def engine(self):
         print('3.0T 190马力 L4')
    def power(self):
         print('1990kW/320N.m')

Audi=AudiCar()
BMW=BMWCar()

发布了46 篇原创文章 · 获赞 37 · 访问量 4509

猜你喜欢

转载自blog.csdn.net/weixin_42444693/article/details/104703122