牢记一句话:大部分使用继承的场合都可以用合成代替,而多重继承则需要不惜一切地避免之。
有三种继承如下:
当你在父类里定义了一个函数但没有在子类中定义的例子时,会发生隐式继承。
class Parent(object): def implicit(self): print "PARENT implicit()" class Child(Parent): pass dad = Parent() son = Child() dad.implicit() son.implicit()
class Child:中的 pass 是在Python中创建空代码块的方法。这样就创建了一个叫 Child 的类,但是没有在里面定义任何细节。在这里它会从它的父类继承所有的行为。运行如下:
将函数放到基类(Parent)中,那么所有的子类(Child)将会自动获得这些函数功能。
需要很多类的时候,可以避免重复写很多代码。
有时候要让子类的函数有一个不同的行为,这样情况隐式继承做不到。需要覆盖子类的函数才行。
例子如下:
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()
这里两个类里面都定义了一个函数,叫做 override ,但是运行结果如下
可以发现子类中新定义的函数 override 取代了父类里面的 override。
使用继承的第三种方法是一个覆盖的特例,这种情况下,你想在父类中定义的内容运行前或者之后再修改行为。
首先要像上个例子一样覆盖函数,然后要接着用 Python 的内置函数 super 来调用父类 Parent 里面的版本。
例子如下:
class Parent(object):
def altered(self):
print "PARENT altered()"
class Child(Parent):
def altered(self):
print "CHILD, BEFORE PARENT altered()"
super(Child, self).altered()
print "CHILD,AFTER PARENT altered()"
dad = Parent()
son = Child()
dad.altered()
son.altered()
运行结果如下:
最重要的是其中的9-11行,当调用 son.altered() 的时候,完成了以下内容:
1.由于覆盖了 Parent.altered ,实际运行的是 Child.altered,所以第9行的输出是意料之中。
2.这里想要在前后加一个动作,所以第9行以后要用 super 来获取 Parent.altered 这个版本。
3.第10行调用了 super(Child, self).altered() ,这和过去用过的 getattr 很相似,不过它还知道你的继承关系,并且会访问到Parent 类。这句话你可以读作 :“用 Child 和 Self 这两个参数调用 super,然后在此返回的基础上调用 altered。”
4.到这里 Parent.altered 就会被运行,然后打印出父类的信息。
5.最后从 Parent.altered 返回到 Child.altered ,打印出后面的信息。
演示一个最终版本,在一个文件中演示三种交互:
#-*-coding:utf-8-*- class Parent(object): def override(self): print "PARENT override()" # 覆盖 def implicit(self): print "PARENT implicit()" # 隐式 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.implicit() # 执行 Parent 类里面的 implicit ,打印"PARENT implicit()" son.implicit() # Child 里面没有定义 implicit ,从 Parent 里面继承 dad.override() # 执行 Parent 类里面的 override ,打印"PARENT override()" son.override() # Child 里面有 override ,打印"CHILD override()" dad.altered() # 执行 Parent 类里面的altered ,打印"PARENT altered()" son.altered() # Child 里面有 altered 打印"CHILD, BEFORE PARENT altered()" # 然后用 Child 和 self 这两个参数调用 super ,在此返回的基础上调用 altered ,这里 # Parent.altered 就会被运行,打印出了"PARENT altered()" # 最后打印"CHILD,AFTER PARENT altered()"
附加了足够的注释,运行结果如下:
======================================================================================================
为什么要用 super()
这里要讲到“多重继承”。多重继承指的是定义的类继承了多个类。比如:
class SuperFun(Child, BadStuff):
pass
这相当于说“创建一个叫 SuperFun 的类,让它同时继承Child 和 BadStuff”。
具体原理没啥意思好像,反正我也用不到。可以看书,这里不写了。
super() 和 __init__搭配使用
最常见的 super() 的用法是在基类的 __init__函数中使用。通常这也是唯一可以进行这种操作的地方。在这里,你在子类里面做了一些事情,然后完成对父类的初始化。下面是一个例子:
class Child(Parent):
def __init__(self, stuff):
self.stuff = stuff
super(Child, self).__init__()
这里和上面的 Child.altered 差别不大,只不过在 __init__ 里面先设置了一个变量,然后才用 Parent.__init__ 初始化了 Parent。
======================================================================================================
合成
继承是一种有用的技术,不过还有一种实现相同功能的方法,就是直接使用别的类和模块,而非依赖于继承。回头来看,前面说了三种继承方式,有两种会通过新代码取代或者修改父类的功能。这其实可以很容易用调用模块里面的函数来实现。
class Other(object):
def override(self):
print "OTHER override()"
def implicit(self):
print "OTHER implicit()"
def altered(self):
print "OTHER implicit()"
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() # print "OTHER implicit()"
son.override() # print "CHILD override()"
son.altered() # print "CHILD, BEFORE OTHER altered()"
# print "OTHER implicit()"
# print "CHILD, AFTER OTHER altered()"
Child 和 Other 里面大部分一样,唯一不同的是,必须定义一个 Child.implicit 函数来完成它的功能。
======================================================================================================
继承和合成的应用场合
指导方法:
1.不惜一切代价避免多重继承,因为它带来的麻烦比能解决的问题都多。
如果非要用,那得准备好钻研类的层次结构,以及花时间去找各种东西的来龙去脉。
2.如果有一些代码会在不同位置和场合应用到,那就用合成来把他们做成模块。
3.只有在代码之间有清楚的关联,可以通过一个单独的共性联系起来的时候使用继承,或者受现有代码或者别的不可抗拒因素 非用不可,那也用吧。
然而,不要成为这些规则的奴隶。