Python继承

注解

继承是一种偷懒的方式.

如果已经有了一个类, 并要创建一个与之很像的类(可能只是新增了几个方法), 该如何办呢? 可以在创建这个新类时, 复制旧类的代码, 但还有更好的方式: 继承.

继承, 是根据已有的类创建新的类. 新类继承了已有类的所有属性和方法, 并可在已有类的基础上添加一些成员(属性和方法), 或修改继承的方法, 通过使用继承机制, 可以轻松实现类的复用.

在Python中, 实现继承的类称为子类,被继承的类称为超类(也可称为基类, 父类).

子类继承超类时, 只需要在定义子类时, 将超类(可以是多个)放在子类之后的圆括号中即可:

class 类名(超类1, 超类2, ...):
    ...
  • 子类继承超类的所有属性和方法;
  • 可以在子类中重写从超类继承的方法.

注解

对于新式类, 如果该类没有显式指定继承自哪个类, 则默认继承object(object类是Python中所有类的超类, 即要么是直接超类, 要么是间接超类).

另外, Python支持多重继承, 即一个子类可以同时拥有多个直接超类.

多重继承

大部分面向对象的编程语言都只支持单继承, 即子类只能有一个父类, 而Python支持多重继承(C++也支持多重继承), 即一个子类可以从多个父类继承. 和单继承相比, 多重继承容易让代码逻辑复杂, 思路混乱, 一直备受争议, Java/C#/PHP等干脆取消了多重继承.

使用多重继承时, 有一点务必注意: 如果多个父类以不同的方式实现了同一个方法(即有多个同名方法), 必须在class语句中小心排列这些父类, 因为位于前面的类中的方法将覆盖位于后面的类的同名方法.

注解

多重继承是一个功能强大的工具, 然而, 除非确实有需要, 否则应该避免使用多重继承, 因为在有些情况下, 它可能带来意外的并发症.

方法解析顺序(MRO)

Python支持(多)继承, 一个类的方法和属性可能定义在当前类, 也可能定义在父类. 对于这种情况, 当调用类方法或者属性时, 就需要对当前类以及它的基类进行搜索, 以确定方法或属性的位置, 而搜索的顺序就称为方法解析顺序.

方法解析顺序(Method Resolution Order), 简称MRO. 对于只支持单继承的编程语言来说, MRO很简单, 就是从当前类开始, 逐个搜索它的父类; 而对于Python, 它支持多重继承, MRO相对会复杂一些.

实际上, Python发展至今, 经历了以下3种MRO算法, 分别是:

  • 从左往右, 采用深度优先搜索(DFS)的算法, 称为旧式类的MRO;
  • 自Python 2.2开始, 新式类在采用深度优先搜索算法的基础上, 对其做了优化;
  • 自Python 2.3开始, 对新式类采用了C3算法. 由于Python 3.x只支持新式类, 所以只支持C3算法.

C3算法可以确保:

  • 父类不比子类先查找;
  • 如果有多个父类, 先查找定义在前面的父类.

一些函数

  • 要确定一个类是否是另一个类的子类, 可以使用内置函数issubclass();
  • 如果想知道一个类的基类, 可以访问其特殊属性__bases__;
  • 要确定一个对象是否是一个类的实例, 可以使用内置函数isinstance().

注解

使用isinstance通常不是良好的做法, 依赖多态在任何情况下都是更好的选择. 一个重要的例外情况是使用抽象基类和模块abc时.