Python3面向对象之多重继承

1 继承

  即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
  子类会继承父类的属性和方法。

1.1 单继承

  每个子类只有一个父类的情况

class people:
    #定义基本属性
    name = ''
    age = 0
    grade= 0
    #定义构造方法
    def __init__(self,n,a,g):
        self.name = n
        self.age = a
        self.grade=g
    def speak(self):
        print("%s 说: 我 %d 岁,读 %d 年级。" %(self.name,self.age,self.grade)) 
#单继承示例
class student(people):
    def __init__(self,n,a,g):
        #调用父类的构函
        people.__init__(self,n,a,g)
 
s = student('ken',10,3)
s.speak()

输出结果:

ken 说: 我 10 岁,读 3 年级。

当我们在子类student里也写入一个speak函数时

class people:
    #定义基本属性
    name = ''
    age = 0
    grade= 0
    #定义构造方法
    def __init__(self,n,a,g):
        self.name = n
        self.age = a
        self.grade=g
    def speak(self):
        print("%s 说: 我 %d 岁,读 %d 年级。" %(self.name,self.age,self.grade)) 
#单继承示例
class student(people):
    def __init__(self,n,a,g):
        #调用父类的构函
        people.__init__(self,n,a,g)
    #覆写父类
    def speak(self):
        print("%s 说: 我 %d 岁" %(self.name,self.age)) 
 
s = student('ken',10,3)
s.speak()

输出结果:

ken 说: 我 10 岁

  可以看到当子类和父类中有同名函数时,调用的speak函数会优先在本类中寻找,所以无法输出父类的speak。我们在编写程序的时候最好避免出现同名函数的情况,如果想要输出同名情况下的父类,可以参考下面这篇博客:
子类调用父类同名方法

1.2 多重继承

  一个子类含有多个父类继承的情况

1.2.1 不引入_init_构造函数情况

  子类C的父类为A和B

class A:
    def f_a(self):
        print('--------A--------')
class B:
    def f_a(self):
        print('--------B--------')
class C(A,B):
    def f_a(self):
        print('--------C--------')
c = C()
c.f_a()

输出结果:

--------C--------

  如果我们想在C类中的f_a方法里面使用父类A或者父类B怎么办呢,下面我们采用一种调用父类的方法实现:

class A:
    def f_a(self):
        print('--------A--------')
class B:
    def f_a(self):
        print('--------B--------')
class C(A,B):
    def f_a(self):
        A.f_a(self)
        B.f_a(self)
        print('--------C--------')
c = C()
c.f_a()

输出结果:

--------A--------
--------B--------
--------C--------

1.2.2 super()机制

  super()机制是调用父类常用的方法,它会默认多继承中从左到右的顺序来调用。
  在多继承的复杂情况中,为了避免同名的属性被覆盖,python会使用一种叫做C3的算法,来控制类属性的查找顺序,即mro查找。在菱形的继承结构中,会采用广度优先遍历的查找循序;非菱形的继承结构中,则会采用深度优先遍历的查找顺序。
  super函数也并不是直接调用父类的方法,它是调用mro顺序里面下一个类的方法。

类名.mro    #可以利用类属性 mro 来查看一个类的继承顺序

class A:
    def f_a(self):
        print('--------A--------')
class B:
    def f_a(self):
        print('--------B--------')
class C(A,B):
    def f_a(self):
        super(C,self).f_a()
        super(A,self).f_a()
        print('--------C--------')
c = C()
c.f_a()
print(C.mro())

输出结果:

--------A--------
--------B--------
--------C--------
[<class ‘main.C’>, <class ‘main.A’>, <class ‘main.B’>, <class ‘object’>]

1.2.3 _init_构造函数

   init()函数是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
  self代表类的实例,self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

class A(object):
    def __init__(self):
        print ('init A...')        
class B(A):
    def __init__(self):
        super().__init__()
        print('init B...')
class C(A):
    def __init__(self):
        super().__init__()
        print ('init C...')
class D(B, C):
    def __init__(self):
        super().__init__()
        print ('init D...')
d=D()
print(D.mro())

输出结果:

init A…
init C…
init B…
init D…
[<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]

  如果你第一次接触多重继承,面对这个结果,你肯定会跟我当初一样的困惑,为什么不是A B C D呢?
  我们知道在多重继承中,遇到父类中函数同名的情况下,默认在class D(B,C)中从左到右执行,且只调用类B一次。而在此处执行完类B后调用类A,返回执行的输出B呀。很遗憾,你被这个多重继承给绕进去了,因为此处调用父类采用的super()函数,它拥有特定的索引顺序,如前文所写。
  所以我们可以把找上一个父类换成找下一个树的对象,这个树的遍历规则满足mro查找。
  D–>B–>C–>A
  类D中的第一个super()找类B,类B中的super()找类C,类C中的super()找类A,然后依次输出并返回,故此处的输出’init A…'是类C直接导致的结果。

下面再看一个实例来领悟

class A():
    def __init__(self):
        print("A")
class B(A):
    def __init__(self):
        print("B")
        super().__init__()
class C(A):
    def __init__(self):
        print("C")
        super().__init__()
class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()

d = D()
print(D.mro())

输出结果:

D
B
C
A
[<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]

  这是一个菱形的继承结构,如果super()是直接调用父类的方法,那么这里输出 B 的后面应该为 A,而实际这里输出为 C,结论就是super函数和父类没有直接关联,它和MRO查找顺序有关。

参考文献

类属性查找顺序——MRO查找
Python 面向对象
python 多重继承
魔法函数__xx__()

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值