继承与派生

1 什么是继承

2 经典类与新式类

3 属性查找

4 子类重用父类的方法

5 继承的实现原理

6 子类中调用父类的方法


 

1 什么是继承

是一种新建类的方式,新建的类称为子类,子类会遗传父类的属性,可以减少代码冗余在python中,子类(派生类)可以继承一个或者多个父类(基类,超类)

class Parent1: #定义父类
    pass
class Parent2(object): #定义父类
    pass
class Sub1(Parent1): #单继承,基类是ParentClass1,派生类是SubClass
    pass
class Sub2(Parent1,Parent2): #python支持多继承,用逗号分隔开多个继承的类
    pass


print(Sub1.__bases__)#__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
print(Sub2.__bases__)
print(Parent1.__bases__)
print(Parent2.__bases__)

2 经典类与新式类

1.只有在python2中才分新式类和经典类,python3中统一都是新式类

2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类

3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类

4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

3 属性查找

先从自己类中查找,再然后再去父类中找...直到最顶级的父类。

#属性查找
class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self): #self=obj
        print('Foo.f2')
        self.f1() #obj.f1()

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

obj=Bar()
print(obj.__dict__)
obj.f2()
'''{}
Foo.f2
Bar.f1
'''

4 子类重用父类的方法

#方法一(不建议用)
class OldboyPeople:
    school = 'Oldboy'

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

    def tell_info(self):
        print('<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex))

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,course,stu_id):
        OldboyPeople.__init__(self,name,age,sex)#调用父类的方法
        self.course=course
        self.stu_id=stu_id

    def learn(self):
        print('%s is learning' %self.name)

    def tell_info(self):
        print('我是学生:',end='')
        # self.tell_info() #stu1.tell_info()
        OldboyPeople.tell_info(self) #调用父类的方法

stu1=OldboyStudent('牛榴弹',18,'male','Python',1)
stu1.tell_info()
#方法二:使用super关键字
class OldboyPeople:
    school = 'Oldboy'

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

    def tell_info(self):
        print('<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex))

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,course):
        # OldboyPeople.__init__(self,name,age,sex)
        super(OldboyStudent,self).__init__(name,age,sex)#使用super关键字
        self.course=course

    def tell_info(self):
        print('我是学生: ',end='')
        # OldboyPeople.tell_info(self)
        super(OldboyStudent,self).tell_info() #使用super关键字

stu1=OldboyStudent('egon',18,'male','python')
# print(stu1.name,stu1.age,stu1.sex,stu1.course)
stu1.tell_info()

5 继承的实现原理

      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中才分新式类与经典类

2、继承原理

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

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

6 子类中调用父类的方法

#方法一:指名道姓,即父类名.父类方法()

#_*_coding:utf-8_*_

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power)
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        Vehicle.run(self)

line13=Subway('中国地铁','180m/s','1000人/箱','',13)
line13.run()
#方法二:super()

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(name,speed,load,power)
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        super(Subway,self).run()

class Mobike(Vehicle):#摩拜单车
    pass

line13=Subway('中国地铁','180m/s','1000人/箱','',13)
line13.run()

 

即使没有直接继承关系,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'>]
#指名道姓
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)
        C.__init__(self)

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

#使用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(D,self).__init__()

f1=D() #super()会基于mro列表,往后找
'''
D的构造方法
B的构造方法
C的构造方法
A的构造方法
'''
指名道姓与super()的区别

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

 

posted on 2018-03-06 13:05  蜗牛也是妞  阅读(268)  评论(0编辑  收藏  举报