一、继承
原则:大部分使用继承的场合都可以用组合取代或者简化,而多重继承则需要不惜一切的避免。
1. 什么是继承
继承:用于指明一个类的大部分或者全部功能都是从一个父类获得的。此时,父类的实例的所有动作可能都会工作在子类的实例上,这样可以把通用的功能放在父类里边,然后给子类设定一些特别的功能。
父类和子类的三种交互方式:
① 子类上的动作完全等同于父类上的动作
② 子类上的动作完全覆盖了父类上的动作
③ 子类上的动作部分替换了父类上的动作
2. 隐式继承
如果在父类中定义了一个函数,而在子类中没有定义,则会发生隐式的继承。也就是说,子类的实例可以调用仅在父类中定义过而在子类中没有定义过的函数。
class Parent(object): def implicit(self):
print("PARENT IMPLICIT()") class Child(Parent):
pass dad = Parent()
son = Child() dad.implicit()
son.implicit()
输出为
PARENT IMPLICIT()
PARENT IMPLICIT()
3. 显式覆盖
如果想要在子类里的函数有不同的功能,则必须使用显式覆盖,这在子类中定义一个同名函数就可以了,如
class Parent(object): def override(self):
print("PARENT OVERRIDE()") class Child(Parent): def override(self):
print("CHILD OVERRIDE()") dad = Parent()
son = Child() dad.override()
son.override()
输出为
PARENT OVERRIDE()
CHILD OVERRIDE()
4. 在运行前或运行后替换
替换是覆盖的一种特例。它允许我们在父类定义的内容运行之前或之后再去修改子类的行为。
其方法为,首先像上例一样覆盖父类的函数,然后用super()调用该函数在父类里的版本。
class Parent(object): def altered(self):
print("PARENT altered()") class Child(Parent): def altered(self):
print("CHILD, BEFORE PARENT altered()") # 显式覆盖
super(Child,self).altered() # 用super(Child,self)调用父类的同名函数
print("CHILD, AFTER PARENT altered()") # 仍为显式覆盖的部分 dad = Parent()
son = Child() dad.altered()
son.altered()
输出为
PARENT altered()
CHILD, BEFORE PARENT altered()
PARENT altered()
CHILD, AFTER PARENT altered()
可见,一方面,我们可以显式覆盖,重写子类同名函数的内容和功能;另一方面,可以用super(Child,self)函数在中间调用父类的同名函数,而它并不影响前后的覆盖内容。
因此,我们可以在子类函数运行前或运行后替换,这取决于super()的位置。
5. 三种方式的组合使用
class Parent(object): def implict(self):
print("PARENT implict()") def override(self):
print("PARENT override()") def altered(self):
print("PARENT altered()") class Child(Parent): def override(self):
print("CHILD override()") def altered(self):
print("CHILD, BEFORE PARENT altered()")
super(Child, self).altered()
print("CHILD, AFTER PARENT altered()") dad = Parent()
son = Child() dad.implict()
son.implict() dad.override()
son.override() dad.altered()
son.altered()
输出为
PARENT implict()
PARENT implict()
PARENT override()
CHILD override()
PARENT altered()
CHILD, BEFORE PARENT altered()
PARENT altered()
CHILD, AFTER PARENT altered()
6. super()函数
① 多重继承
指定义的类继承了一个或多个类,例如
class SuperFun(Child, BadStuff):
pass
此时,一旦在SuperFun的实例上调用任何隐式动作,Python就必须返回到Child和BadStuff的类层次结构中去查找可能的函数,而且必须用固定的顺序去查找。
为此,Python使用了“Method Resolution Oder(方法解析顺序),MRO”+C3算法,并提供super()函数,用于实现这些功能。这样,就可以方便的使用super()来查询各父类的函数了。
② super()与__init__的搭配使用
super()最常见的用法是,在父类的__init__函数中使用。它使得我们可以先在子类中实现一些功能,然后再让父类初始化,例如
class Child(Parent): def __init__(self, stuff):
self.stuff = stuff
super(Child, self).__init__()
二、组合
组合能够实现和继承几乎完全相同的功能,但它采用的方法并非是隐式继承,而是通过调用模块里的函数来实现。
class Other(object): def implicit(self):
print("OTHER implicit()") def override(self):
print("OTHER override()") def altered(self):
print("OTHER altered()") class Child(object): def __init__(self):
self.other = Other() def implicit(self):
self.other.implicit() def override(self):
print("CHILD override()") def altered(self):
print("CHILD, BEFORE OTHER altered()")
self.other.altered()
print("CHILD, AFTER OTHER altered()") son = Child() son.implicit()
son.override()
son.altered()
输出为
OTHER implicit()
CHILD override()
CHILD, BEFORE OTHER altered()
OTHER altered()
CHILD, AFTER OTHER altered()
可见,这里并不是为Child指定了一个父类,而是将父类的功能打包为一个模块(实质也是一个类),然后直接调用模块里面的函数,这也能够完成和继承相同的功能。
三、继承和组合的应用场合
继承和组合事实上都是为了解决代码的复用问题。前者通过创建一种让我们在子类里隐含父类的功能的机制来解决这个问题,后者通过模块/类中的函数调用达到了相同的目的。关于二者的选择,大体上有3个指导原则:
1. 避免多重继承。多重继承复杂且不可靠,如果要使用,需要钻研一下类层次结构及其来龙去脉。
2. 如果有一些代码会在不同位置和场合应用到,就用组合将它们做成模块。
3. 只有在代码的可复用部分之间有清楚的关联,可以通过一个单独的共性连接起来的时候,才使用继承。
【巩固练习】代码风格参考 PEP 8 -- Style Guide for Python Code
【注意】在有的语言如JavaScript中,对象其实是类的副本,这样的语言叫原型(prototype)语言,这种语言中类和对象除了用法以外没多少不同。但在Python中,类则比较像是用来创建对象的模板。