继承与派生

Posted 蜗牛也是妞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承与派生相关的知识,希望对你有一定的参考价值。

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列表)

 

以上是关于继承与派生的主要内容,如果未能解决你的问题,请参考以下文章

继承与派生

继承与派生

类的继承与派生

面向对象之继承与派生

类的继承与派生

继承与派生知识点