Python继承机制及其使用

继承是面向对象的三大特征之一,也是实现代码复用的重要手段。继承经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类。

例如,有一个 Shape 类,该类的 draw() 方法可以在屏幕上画出指定的形状,现在需要创建一个 Rectangle 类,要求此类不但可以在屏幕上画出指定的形状,还可以计算出所画形状的面积。要创建这样的 Rectangle 类,除了将 draw() 方法直接复制到新类中,并添加计算面积的方法,其实还有更简单的方法,即让 Rectangle 类继承 Shape 类,这样当 Rectangle 类对象调用 draw() 方法时,Python 解释器会自动去 Shape 类中调用该方法,如此,我们只需在 Rectangle 类中添加计算面积的方法即可。

Python 中,实现继承的类称为子类,被继承的类称为父类(也可称为基类超类)。子类继承父类的语法是:在定义子类时,将多个父类放在子类之后的圆括号里。语法格式如下:

class 类名(父类1, 父类2, ...):
    #类定义部分

注意,Python 的继承是多继承机制,即一个子类可以同时拥有多个直接父类。

从上面的语法格式来看,定义子类的语法非常简单,只需在原来的类定义后增加圆括号,并在圆括号中添加多个父类,即可表明该子类继承了这些父类。如果在定义一个 Python 类时,并未显式指定这个类的直接父类,则这个类默认继承 object 类。

object 类是所有类的父类,要么是直接父类,要么是间接父类。

父类和子类的关系,就好像水果和苹果的关系,苹果是一种特殊的水果,苹果是水果的子类,水果是苹果的父类,因此可以说,苹果继承了水果。不仅如此,由于子类是一种特殊的父类,因此父类包含的范围总比子类包含的范围要大,所以可以认为父类是大类,而子类是小类。

从实际意义上看,子类是对父类的扩展,子类是一种特殊的父类。从这个意义上看,使用继承来描述子类和父类的关系是错误的,用扩展更恰当。因此,这样的说法更加准确:苹果扩展了水果这个类。

从子类的角度来看,子类扩展(extend)了父类;但从父类的角度来看,父类派生(derive)出子类。也就是说,扩展和派生所描述的是同一个动作,只是观察角度不同而已。


下面程序示范了子类继承父类的特点。下面是 Fruit 类的代码:
class Fruit:
    def info(self):
        print("我是一个水果!重%g克" % self.weight)

class Food:
    def taste(self):
        print("不同食物的口感不同")

# 定义Apple类,继承了Fruit和Food类
class Apple(Fruit, Food):
    pass

# 创建Apple对象
a = Apple()
a.weight = 5.6
# 调用Apple对象的info()方法
a.info()
# 调用Apple对象的taste()方法
a.taste()
运行结果为:

我是一个水果!重5.6克
不同食物的口感不同

上面程序中,定义了两个父类:Fruit 类和 Food 类,接下来定义了一个 Apple 类,该类本身是空类,但其继承自 Food 类和 Fruit 类。因此,在主程序部分创建了 Apple 对象之后,可以访问 Apple 对象的 info() 和 taste() 方法,这表明 Apple 对象也具有了 info() 和 taste() 方法,这就是继承的作用,即子类扩展(继承)了父类,将可以继承得到父类定义的方法,这样子类就可复用父类的方法了。

关于Python的多继承

大部分面向对象的编程语言(除了 C++)都只支持单继承,而不支持多继承,这是由于多继承不仅增加了编程的复杂度,而且很容易导致一些莫名的错误。

Python 虽然在语法上明确支持多继承,但通常推荐如果不是很有必要,则尽量不要使用多继承,而是使用单继承,这样可以保证编程思路更清晰,而且可以避免很多麻烦。

当一个子类有多个直接父类时,该子类会继承得到所有父类的方法,这一点在前面示例中己经做了示范。现在的问题是,如果多个父类中包含了同名的方法,此时会发生什么呢?此时排在前面的父类中的方法会“遮蔽”排在后面的父类中的同名方法。例如如下代码:
class Item:
    def info (self):
        print("Item中方法:", '这是一个商品')
class Product:
    def info (self):
        print("Product中方法:", '这是一个工业产品')
class Mouse(Item, Product): # ①
    pass
m = Mouse()
m.info()
上面 ① 号代码让 Mouse 继承了 Item 类和 Product 类,由于 Item 排在前面,因此 Item 中定义的方法优先级更好,Python 会优先到 Item 父类中搜寻方法,一旦在 Item 父类中搜寻到目标方法,Python 就不会继续向下搜寻了。

运行上面程序,将看到如下输出结果:

Item 中方法:这是一个商品

如果将程序中第 7 行代码改为如下形式:

class Mouse(Product,Itern): #①

此时 Product 父类的优先级高于 Item 父类,因此 Product 中的 info() 方法将会起作用。运行上面程序,将会看到如下输出结果:

Product中方法:这是一个工业产品