面向对象(二)继承、派生

继承

继承是一种新建类的方式,新建的类称为子类或者派生类,父类又可以称为基类或者超类

从字面意思来讲可以理解,子类可以来遗传父类的属性,我们在平时使用继承主要就是为了减少代码的冗余。在我们定义类的时候,类与类之间那些对象不同的属性就可能会有相同的重复代码。继承就是解决这种类与类之间的关系,寻找这种关系需要先抽象在继承

继承方式

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

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

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

查看继承

>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

继承与抽象(先抽象再继承)

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

 

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

 

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

 

属性查找

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

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

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

obj=Bar()

obj.f2() 
#  Foo.f2
#  Bar.f1

派生

子类自己定义新的属性,如果与父类同名以子类为准

class Riven(Hero):
    camp='Noxus'
    def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        print('from riven')
    def fly(self): #在自己这里定义新的
        print('%s is flying' %self.nickname)

在子类中派生出的新方法中重用父类的功能:

1、指名道姓的调用(与继承没有关系)

# class OldboyPeople:
#     def __init__(self,name,age,sex):
#         self.name=name
#         self.sex=sex
#         self.age=age
#     def tell_info(self):
#         print('''
#         ============个人信息===========
#         姓名:%s
#         年龄:%s
#         性别:%s
#         '''%(self.name,self.age,self.sex))
# class Teacher(OldboyPeople):
#     def __init__(self,name,age,sex,salary,level):
#         OldboyPeople.__init__(self,name,age,sex)
#         self.salary=salary
#         self.level=level
#     def teach(self):
#         print('%s 讲课'%self.name)
#     def tell_info(self):
#         OldboyPeople.tell_info(self)
#         print('''
#         薪资:%s
#         级别:%s
#         '''%(self.salary,self.level))
#
# class Student(OldboyPeople):
#     def choice(self):
#         print('%s 选课'%self.name)
#
# tea1=Teacher('egon',18,'male',40,9)
# stu1=Student('alex',38,'male')
#
# tea1.teach()
# tea1.tell_info()
# stu1.choice()
# stu1.tell_info()
使用类名调用

2、super()调用(严格依赖于继承)

super()的返回值是一个特殊的对象,该对象专门用来调用父类中的属性

# class OldboyPeople:
#     def __init__(self,name,age,sex):
#         self.name=name
#         self.sex=sex
#         self.age=age
#     def tell_info(self):
#         print('''
#         ============个人信息===========
#         姓名:%s
#         年龄:%s
#         性别:%s
#         '''%(self.name,self.age,self.sex))
# class Teacher(OldboyPeople):
#     def __init__(self,name,age,sex,salary,level):
#         super().__init__(name,age,sex)
#         self.salary=salary
#         self.level=level
#     def teach(self):
#         print('%s 讲课'%self.name)
#     def tell_info(self):
#         super().tell_info()
#         print('''
#         薪资:%s
#         级别:%s
#         '''%(self.salary,self.level))
#
# class Student(OldboyPeople):
#     def choice(self):
#         print('%s 选课'%self.name)
#
# tea1=Teacher('egon',18,'male',40,9)
# stu1=Student('alex',38,'male')
#
# tea1.teach()
# tea1.tell_info()
# stu1.choice()
# stu1.tell_info()
super()调用

了解知识:1、在python2当中super()的调用方式:super(自己的类名,self)

                  2、即使没有直接继承关系,super仍然会按照mro继续往后查找

#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
    def test(self):
        super().test()
class B:
    def test(self):
        print('from B')
class C(A,B):
    pass

c=C()
c.test() #打印结果:from B


print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
没有继承关系,基于mro查找

经典类和新式类

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

继承实现原理

1、继承顺序

 

                             

class A(object):
    def test(self):
        print('from A')

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

class C(A):
    def test(self):
        print('from C')

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

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

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

继承顺序
View Code

2、继承原理

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

>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。


而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:


1.子类会先于父类被检查

 

2.多个父类会根据它们在列表中的顺序被检查


3.如果对下一个类存在两个合法的选择,选择第一个父类

 当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值