类的继承、派生和组合

一、继承

继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又称之为基类或超类,新建的类称为派生类或子类。

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

# 查看继承:
print( SubClass1.__bases__)
print(SubClass2.__bases__)

打印结果:
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

二、继承属性的查找顺序

lass Foo:
    def f1(self):
        print('from Foo.f1')

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

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

print(Bar.__bases__)    # 查看继承的父类

b = Bar()

b.f1()    # 从b本身的名称空间查找
打印结果:from Bar.f1

b.f2() 
打印结果:
from Foo.f2
from Bar.f1 
  
由于b对象本身没有f2,开始从Bar类的名称空间查找f2,Bar类的名称空间也没有f2,则从其父类Foo名称空间查找f2程序运行到f2()下面的self.f1()这句时此时这句可改写为b.f1(),运行到这行时即从b对象本身名称空间查找f1,而不是运行Foo类名称空间中的f1

# 查看继承属性的顺序
print(Bar.mro())   # 以列表形式打印
打印结果:
[<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>] 

三、经典类和新类

# 经典类:无继承objict的类,已经该类的子类
 >>> class A:pass
...
>>> A.__bases__
()
>>> class B(A):pass
...
>>> B.__bases__
(<class __main__.A at 0x0000000002D19828>,)

# 新类:
>>> A.__bases__
(<type 'object'>,)
>>> class B(A):pass

1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

注意:在python中如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

四、继承的实现原理

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1、子类会先于父类被检查
2、多个父类会根据它们在列表中的顺序被检查
3、如果对下一个类存在两个合法的选择,选择第一个父类

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先

1、深度优先
深度优先是按照如下顺序去查找属性的,直到找到该属性。即先安一个分支一直找到顶层的父类,然后在返回来找另外的分支
当类是经典类时,多继承情况下,在查找属性不存在时,会按照深度优先的方式查找下去

2、广度优先
广度优先是按照如下顺序去查找属性的,直到找到该属性。即先按照一个分支去查找,直到遇到顶层的父类后返回,查找下一个分支,最后查找顶层的父类
当类是经典类时,多继承情况下,在查找属性不存在时,会按照广度优先的方式查找下去
在这里插入图片描述

class A:
    def text(self):
        print('from A')


class B(A):
    # def text(self):
    #     print('from B')
    pass

class C(A):
    # def text(self):
    #     print('from C')
    pass

class D(B):
    # def text(self):
    #     print('from D')
    pass

class E(B):
    # def text(self):
    #     print('from E')
    pass

class F(C):
    # def text(self):
    #     print('from F')
    pass

class G(C):
    # def text(self):
    #     print('from G')
    pass

class H(D,E):
    # def text(self):
    #     print('from H')
    pass
class I(F,G):
    # def text(self):
    #     print('from I')
    pass

class J(H,I):
    # def text(self):
    #     print('from J')
    pass
    
a = J()
print(J.mro())
a.text()
输出结果:[<class '__main__.J'>, <class '__main__.H'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.I'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
from A

在这里插入图片描述

五、在子类中重用父类的方法和属性

在子类中重用父类的方法和属性是为了减少重复代码,重用共有两种方法。

1、指名道姓,即父类名.父类方法()

方法一:指名道姓法(不依赖继承)
class Hero:
    # 定义每个英雄的名字,生命值,攻击力,防御
    def __init__(self, name, life_vale, aggressivity, physical_defense, magic_defense):
        self.name = name
        self.life_vale = life_vale
        self.aggressivity = aggressivity
        self.physical_defense = physical_defense
        self.magic_defense = magic_defense

# 定义一个生产英雄的类
class ADC(Hero):
    camp = 'high aggressivity'

    def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
        Hero.__init__(self,name, life_vale, aggressivity, physical_defense, magic_defense)   # 指明要调用父类的那些属性和方法
        self.weapon = weapon

    # 定义英雄的技能和技能的伤害值
    def attack(self,enemy):
        enemy.life_vale = enemy.life_vale - self.aggressivity * (1-enemy.physical_defense) * 2


class AP(Hero):
    camp = 'high skill aggressivity'

    def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
        Hero.__init__(self,name, life_vale, aggressivity, physical_defense, magic_defense) # 指明要调用父类的那些属性和方法
        self.weapon = weapon

    def skill(self, enemy):
        enemy.life_vale = enemy.life_vale - self.aggressivity * (1 - enemy.magic_defense)

adc = ADC('艾希',2000,300,0.3,0.4,'arrow')
ap = AP('维克托',2000,90,0.4,0.3,'truncheon')

print(adc.__dict__)
print(ap.__dict__)
输出结果:
{'name': '艾希', 'life_vale': 2000, 'aggressivity': 300, 'physical_defense': 0.3, 'magic_defense': 0.4, 'weapon': 'arrow'}
{'name': '维克托', 'life_vale': 2000, 'aggressivity': 90, 'physical_defense': 0.4, 'magic_defense': 0.3, 'weapon': 'truncheon'}

2、super()

# 定义一个生产英雄的类
class ADC(Hero):
    camp = 'high aggressivity'

    def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
        super(ADC, self).__init__(name, life_vale, aggressivity, physical_defense, magic_defense)
        # 此种方法依赖继承,这句其本质上是类的属性绑定
        self.weapon = weapon

    # 定义英雄的技能和技能的伤害值
    def attack(self,enemy):
        enemy.life_vale = enemy.life_vale - self.aggressivity * (1-enemy.physical_defense) * 2


class AP(Hero):
    camp = 'high skill aggressivity'

    def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
        super().__init__(name, life_vale, aggressivity, physical_defense, magic_defense)
        self.weapon = weapon

    def skill(self, enemy):
        enemy.life_vale = enemy.life_vale - self.aggressivity * (1 - enemy.magic_defense)

adc = ADC('艾希',2000,300,0.3,0.4,'arrow')
ap = AP('维克托',2000,90,0.4,0.3,'truncheon')

print(adc.__dict__)
print(ap.__dict__)
输出结果:
{'name': '艾希', 'life_vale': 2000, 'aggressivity': 300, 'physical_defense': 0.3, 'magic_defense': 0.4, 'weapon': 'arrow'}
{'name': '维克托', 'life_vale': 2000, 'aggressivity': 90, 'physical_defense': 0.4, 'magic_defense': 0.3, 'weapon': 'truncheon'}

3、 super()继承原理

class A:

    def f1(self):
        print('from A')
        super().f3()

class B:

    def f2(self):
        print('from B')

class D:

    def f3(self):
        print('from D')
        # super().f2()

class C(A,B,D):
    pass

c = C()
print(C.mro())
c.f1()

输出结果:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class 'object'>]
from A
from D

由于c中没有f1就按照C中的mro列表顺序到A中查找f1,A中有f1则执行f1的函数,首先打印出来 ’from A‘此时程序继续向下走开始执行super().f3(),由于super()具基于C的mro列表查找顺序继续向下查找的性质,开始到B中查找f3,B中没有f3则继续向下到D中查找f3,在D中找到f3,输出结果

六、组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

# 定义人类
class People:

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

# 定义老师类
class Teacher(People):

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

# 定义学生类
class Student(People):

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

# 定义学生类
class Course:

    def __init__(self,course_name,course_price,course_period):
        self.course_name = course_name
        self.course_price = course_price
        self.course_period = course_period

    def course_info(self):
        info = '''
                    课程:%s
                    价格:%s
                    时长:%s        
            '''%(self.course_name,self.course_price,self.course_period)
        print(info)

class Data:

    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day

    def data_info(self):
        print('%s-%s-%s'%(self.year,self.month,self.day))

teacher1 = Teacher('刘淑','女',26,50,10000)      # 实例化老师类对象
teacher2 = Teacher('夏天','男',28,70,20000)

student1 = Student('王战','男',30,'07:30:00')
student2 = Student('狗熊','男',30,'09:30:00')      # 实例化学生类对象

python = Course('python',12000,'6 months')       # 实例化课程类对象
linux = Course('linux',8000,'5 months')

data = Data(1993,5,16)

student1.course = python
student2.course1 = python
student2.course2 = linux
student2.course1.course_info()                 # 将课程对象组合到学生对象中:组合是两个对象的关系为 xx 有 xx
student1.datas = data
student1.datas.data_info()

1.继承的方式

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

2.组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值