Python 系列学习十二:面向对象编程 - 多重继承

前言

打算写一系列文章来记录自己学习 Python 3 的点滴;本章主要介绍 Python 有关面向对象编程中的多重继承方面的内容;

本文为作者的原创作品,转载需注明出处;

导读

在笔者打算开始梳理这块内容的时候,觉得,如果匆匆忙忙的去撰写一篇功能性的文章,比如如何用 Python 去实现多重继承,如何使用 Mixin 等,就失去了笔者撰写此篇博文的意义了;继承原本就是面向对象编程最核心的思想,由此衍生出了多态等等一系列反应世界特征的特性;由此,笔者更加关注的是,继承多重继承的本质是什么?为什么 Java 是单继承加接口的方式?而 C++、Python 却是多重继承的实现方式?这样,我们才能站在原点,既是理论的原点去认知事物,才能洞悉事物的本质;

所以,笔者首先要思考的是,Python 的多重继承的意义何在?类比,是人类认知事物最快的方式,所以,我们先来看看 Java 的继承方式,Java 语言的是单继承模式,不过通过 Java 接口,为子类扩展除了其它类型的特征,相当于实现了多继承的方式,只是,Java 通过这种方式,强调了主次的关系,既是说,某一个对象,它必须只有一个主要的父类,其它的额外的类型都是其附加属性,这就是 Java 的面向对象设计的核心思想;为什么要这么来做呢?当然,大家会想到,这样设计使得程序设计更简单;其实,还有一层意思,这种设计的方式更符合现实世界的特征,世界上,大概99%的对象,它们都有且仅有一个主的父类,而其它的类都应该作为其附加属性存在,比如,小狗,它的主父类一定是哺乳动物,小鸟,它的主父类一定是鸟科动物,而小狗会跑(Runnable)的特性类型应当作为它的附加属性,小鸟会飞(Flyable)的特性同样应当作为它的附加属性;所以,Java 语言的设计者充分考虑到了这一点,通过单继承和接口的设计方式,完美的诠释了世界的本质特征;

而 Python,Python 的程序设计中,并没有接口的概念,它又是怎么来实现这种主次的继承关系的呢?Ok,带着这个本质问题,笔者将带领大家来认识 Python 的多重继承的设计方式以及它为什么这么设计;

多重继承

现实中的例子

上面用大篇幅描述了 Java 继承的设计理念,以一种主次的继承关系来描绘真实的世界;那么下面,笔者就用一个最简单的例子,来阐述这种关系;客机(Airliner)首先它是一种交通工具(Vehicle),其次它会飞(Flyable);客车(Bus)也是一种交通工具(Vehicle),其次它会跑(Runnable);AirlinerBus的继承关系可以用下面这张图来描绘,

Airliner同时继承自VehicleFlyableBus同时继承自VehicleRunnable;不过,虽然都同时继承,但是大家也可以一眼就能够分辨的出,谁是谁是,当然,VehicleFlyableRunnable,因为它们提供的是附属的属性功能;那么面向对象的语言中,它们是被如何设计来满足这个现实中的例子的呢?

Java 的实现

针对上述现实中的例子,Java 语言将主要的继承关系Vehicle类 Class的方式进行表述,而次要的继承关系,诸如FlyableRunnable接口 Interface的方式来表述;因此,我们有如下的编程实现;

首先,定义要的继承关系既是Vehicle类;

Vehicle.java

1
2
3
4
5
6
7
8
9
public abstract class Vehicle {

// 返回该交通工具的名字
public abstract String getName();

// return 载客容量
public abstract int getCarryingCapacity();

}

其次,定义要的继承关系,FlyableRunnable接口,分别提供附属的属性方法flyrun

Flyable.java

1
2
3
public interface Flyable {
public void fly();
}

Runnable.java

1
2
3
public interface Runnable{
public void run();
}

最后,分别实现AirplaneBus

Airplane.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Airplane extends Vehicle implements Flyable {

public String getName(){
return "Airplane";
}

public int getCarryingCapacity(){
return 300;
}

public void fly() {
System.out.println("Hi, I'm "+getName()+", I am flying with totally "+getCarryingCapacity() +" passengers!");
}
}

Bus.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Bus extends Vehicle implements Runnable {

public String getName(){
return "Bus";
}

public int getCarryingCapacity(){
return 30;
}

public void run() {
System.out.println("Hi, I'm "+getName()+", I am running with totally "+getCarryingCapacity() +" passengers!");
}
}

这样,Java 通过抽象类接口完美的诠释了这种主次的继承关系;那么 Python 呢?

Python 的实现

因为 Python 没有类似 Java 的接口的概念,在诠释这种主次的继承关系的时候,不如 Java 这么直观;先来看看 Python 的多重继承是如何实现这种多个父类的继承关系的;

多重继承

由于 Python 没有类似 Java 的接口,所以,VehicleFlyable以及Runnable均以的形式出现;下面,我们来看看 Python 是如何实现这种多重继承的关系的;

Vehicle

1
2
3
4
5
class Vehicle(object):
def getName(self):
pass
def getCarryingCapacity(self):
pass

通过空的方法体告诉开发者,Vehicle应当被视为是一个抽象类

Flyable

1
2
3
class Flyable(object):
def fly(self):
pass

同样,通过空方法体告诉开发者,fly() 方法是一个抽象方法需要继承它的子类去实现;

Runnable

1
2
3
class Runnable(object):
def run(self):
pass

同样,通过空方法体告诉开发者,run() 方法是一个抽象方法需要继承它的子类去实现;

Airline

1
2
3
4
5
6
7
class Airline(Vehicle, Flyable):
def getName(self):
return "Airline"
def getCarryingCapacity(self):
return 300
def fly(self):
print("Hi, I'm %s, I am flying with totally %s passengers!" % (self.getName(), self.getCarryingCapacity() ) )

Bus

1
2
3
4
5
6
7
class Bus(Vehicle, Runnable):
def getName(self):
return "Bus"
def getCarryingCapacity(self):
return 30
def run(self):
print("Hi, I'm %s, I am flying with totally %s passengers!" % (self.getName(), self.getCarryingCapacity() ) )

是的,通过上述的方式,我们分别实现了AirlineBus的多重继承关系;但是,问题是,它没有分清主次继承之间的关系;这也就是为什么 Mixin 出现的原因;

采用 Mixin

看看我们通过Mixin来如何描绘这种主次的继承关系的?很简单,只需要对上述的代码稍作修改该即可,

首先将 Flyable 改为 FlyableMixin

1
2
3
class FlyableMixin(object):
def fly(self):
pass

同样的,将 Runnable 改为 RunnableMixin

1
2
3
class RunnableMixin(object):
def run(self):
pass

奇怪了,除了名字以外,没有改变什么东西呀?是的,你说对了,只是类名上做了修改该;然后,修改对应的 AirlineBus

1
2
class Airline(Vehicle, FlyableMixin):
...
1
2
class Bus(Vehicle, RunnableMixin):
...

逗我了,除了类名的修改,你还能干点啥?呵呵,不过笔者想说的是,上面的修改就是Mixin的全部了,其实也没什么好奇怪的,Python 语言本身就是处处充满了约定,而不是强制约束和要求,它这里的意思就是说,如果想要描述这种主次的继承关系,那好,只需要在命名的时候记得加上后缀Mixin就可以了,当然,Python 其实还想要告诉我们的是,正如导读中所描述的那样,99%现实世界中的对象满足这种主次的继承关系,那始终还有那1%现实中的对象是不满足的,而这个数量其实也并不小,不能小觑,所以呢,Python 不对主次的继承关系做强制性的约束,而只是一个简单的约定,这样,从多重继承的方式上可以满足剩下的那1%

Reference

http://www.bjhee.com/python-mixin.html