python面向对象

面向对象的三大特性是指:封装、继承和多态
python中类分为经典类和新式类,如果一个类继承了多个类,那么其寻找方法的方式有两种:深度优先广度优先
本文只介绍python3中多继承场景。

python多继承

什么是多继承
继承是面向对象编程的一个重要的方式 ,通过继承 ,子类就可以扩展父类的功能,在 python 中一个类能继承自不止一个父类 ,这叫做 python 的多重继承(Multiple Inheritance )。

钻石继承(菱形继承)问题

多继承容易导致钻石继承(菱形继承)问题 ,关于为什么会叫做钻石继承问题 ,看下图就知道了 :


image.png

在 python 中 ,钻石继承首先体现在父类方法的调用顺序上 ,比如若B和C同时重写了 A 中的某个方法时 :

class A(object):
    def m(self):
        print("m of A called")

class B(A):
    def m(self):
        print("m of B called")

class C(A):
    def m(self):
        print("m of C called")

class D(B,C):
    pass

如果我们实例化 D 为 d ,然后调用 d.m() 时 ,输出 "m of B called",如果 B 没有重写 A 类中的 m 方法时 :

class A(object):
    def m(self):
        print("m of A called")

class B(A):
    pass

class C(A):
    def m(self):
        print("m of C called")

class D(B,C):
    pass

此时调用 d.m 时,则会输出 "m of C called" , 那么如何确定父类方法的调用顺序呢 ,这一切的根源还要讲到方法解析顺序(Method Resolution Order,MRO)。
钻石继承还有一个问题是 ,比如若 B 和 C 中的 m 方法也同时调用了 A 中的m方法时 :

class A:
    def m(self):
        print("m of A called")

class B(A):
    def m(self):
        print("m of B called")
        A.m(self)

class C(A):
    def m(self):
        print("m of C called")
        A.m(self)

class D(B,C):
    def m(self):
        print("m of D called")
        B.m(self)
        C.m(self)
# 输出结果:
# 此时我们调用 d.m ,A.m 则会执行两次。
m of D called
m of B called
m of A called
m of C called
m of A called

这种问题最常见于当我们初始化 D 类时 ,那么如何才能避免钻石继承问题呢 ?

super and MRO

其实上面两个问题的根源都跟 MRO 有关 ,MRO(Method Resolution Order) 也叫方法解析顺序 ,主要用于在多继承时判断调的属性来自于哪个类 ,其使用了一种叫做 C3 的算法 ,其基本思想是在避免同一类被调用多次的前提下 ,使用广度优先和从左到右的原则去寻找需要的属性和方法 。当然感兴趣的同学可以移步 :MRO介绍 。

比如针对如下的代码 :

>>> class F(object): pass
>>> class E(object): pass
>>> class D(object): pass
>>> class C(D,F): pass
>>> class B(D,E): pass
>>> class A(B,C): pass

当你打印 A.mro 时可以看到输出结果为 :

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)

如果我们实例化 A 为 a 并调用 a.m() 时 ,如果 A 中没有 m 方法 ,此时python 会沿着 MRO 表逐渐查找 ,直到在某个父类中找到m方法并执行 。
那么如何避免顶层父类中的某个方法被执行多次呢 ,此时就需要super()来发挥作用了 ,super 本质上是一个类 ,内部记录着 MRO 信息 ,由于 C3 算法确保同一个类只会被搜寻一次 ,这样就避免了顶层父类中的方法被多次执行了 ,比如针对钻石继承问题 2 中的代码可以改为 :

class A(object):
    def m(self):
        print("m of A called")

class B(A):
    def m(self):
        print("m of B called")
        super().m()

class C(A):
    def m(self):
        print("m of C called")
        super().m()

class D(B,C):
    def m(self):
        print("m of D called")
        super().m()

此时打印的结果就变成了 :

m of D called
m of B called
m of C called
m of A called
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  •   面向对象是个老的主题了,但不同语言中对面向对象的语法实现仍然体现了面向对象不是一个简单的思想,实际对其理解与应...
    杨强AT南京阅读 271评论 0 2
  • > 面向对象的三大特性:封装,继承,多态 ## 封装 > 封装就是使用特殊的语法,对成员属性和成员方法进行包装,达...
    lmonkey_01阅读 328评论 0 0
  • 面向对象 创建类 通过class关键字定义,类名最好以大写字母开头,举例: 实例化 直接调用类即可实例化,举例: ...
    dawsonenjoy阅读 572评论 0 1
  • 今日内容 面向对象的三大特性 封装 继承与派生 多态与多态性 绑定方法与非绑定方法 内置的方法 元类 异常处理 内...
    徐弱西阅读 223评论 0 0
  • 面向对象的特征——继承 继承是面向对象的重要特征之一,继承是两个类或者多个类之间的父子关系,子进程继承了父进程的所...
    云Shen不知处阅读 352评论 0 0