继承(Inheritance)是面向对象软 件技术当中的一个概念。如果一个类别 A“继承自”另一个类别 B,就把这个 A 称为“B 的子类别”,而把 B 称为“A 的父类别”,也可以称“B 是 A 的超类”。
继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 (源自维基百科)
由上面对继承的表述,可以简单总结出继承的意图或者好处:
1、可以实现代码重用,但不是仅仅实现代码重用,有时候根本就没有重用
2、实现属性和方法继承
#!/usr/bin/env Python
# coding=utf-8
__metaclass__ = type
class Person:
def speak(self):
print "I love you."
def setHeight(self, n):
self.length = n
def breast(self, n):
print "My breast is: ",n
class Girl(Person):
def setHeight(self):
print "The height is:1.70m ."
if __name__ == "__main__":
cang = Girl()
cang.setHeight()
cang.speak()
cang.breast(90)
保存,执行$ python 20901.py
The height is:1.70m .
I love you.
My breast is: 90
对以上程序进行解释,从中体会继承的概念和方法。
首先定义了一个类 Person,在这个类中定义了三个方法。注意,没有定义初始化函数,初始化函数在类中不是必不可少的。
然后又定义了一个类 Girl,这个类的名字后面的括号中,是上一个类的名字,这就意味着 Girl 继承了 Person,Girl 是 Person 的子类,Person 是 Girl 的父类。
既然是继承了 Person,那么 Girl 就全部拥有了 Person 中的方法和属性(上面的例子虽然没有列出属性)。但是,如果 Girl 里面有一个和 Person 同样名称的方法,那么就把 Person 中的同一个方法遮盖住了,显示的是 Girl 中的方法,这叫做方法的重写。
实例化类 Girl 之后,执行实例方法 cang.setHeight(),由于在类 Girl 中重写了 setHeight 方法,那么 Person 中的那个方法就不显作用了,在这个实例方法中执行的是类 Girl 中的方法。
虽然在类 Girl 中没有看到 speak 方法,但是因为它继承了 Person,所以 cang.speak() 就执行类 Person 中的方法。同理 cang.breast(90),它们就好像是在类 Girl 里面已经写了这两个方法一样。既然继承了,就是我的了。
多重继承
所谓多重继承,就是只某一个类的父类,不止一个,而是多个。比如:#!/usr/bin/env Python
# coding=utf-8
__metaclass__ = type
class Person:
def eye(self):
print "two eyes"
def breast(self, n):
print "The breast is: ",n
class Girl:
age = 28
def color(self):
print "The girl is white"
class HotGirl(Person, Girl):
pass
if __name__ == "__main__":
kong = HotGirl()
kong.eye()
kong.breast(90)
kong.color()
print kong.age
在这个程序中,前面有两个类:Person 和 Girl,然后第三个类 HotGirl 继承了这两个类,注意观察继承方法,就是在类的名字后面的括号中把所继承的两个类的名字写上。但是第三个类中什么方法也没有。
然后实例化类 HotGirl,既然继承了上面的两个类,那么那两个类的方法就都能够拿过来使用。保存程序,运行一下看看
$ python 20902.py
two eyes
The breast is: 90
The girl is white
28
值得注意的是,这次在类 Girl 中,有一个 age = 28,在对 HotGirl 实例化之后,因为继承的原因,这个类属性也被继承到 HotGirl 中,因此通过实例属性 kong.age 一样能够得到该数据。
由上述两个实例,已经清楚看到了继承的特点,即将父类的方法和属性全部承接到子类中;如果子类重写了父类的方法,就使用子类的该方法,父类的被遮盖。
多重继承的顺序
多重继承的顺序很必要了解。比如,如果一个子类继承了两个父类,并且两个父类有同样的方法或者属性,那么在实例化子类后,调用那个方法或属性,是属于哪个父类的呢?造一个没有实际意义,纯粹为了解决这个问题的程序:
#!/usr/bin/env Python
# coding=utf-8
class K1(object):
def foo(self):
print "K1-foo"
class K2(object):
def foo(self):
print "K2-foo"
def bar(self):
print "K2-bar"
class K3(object):
pass
class K4(object):
def foo(self):
print "K4-foo"
class J1(K1, K2):
pass
class J2(K3, K4):
def bar(self):
print "J2-bar"
class C(J1, J2):
pass
if __name__ == "__main__":
print C.__mro__
m = C()
m.foo()
m.bar()
这段代码,保存后运行:
[root@FTP codes]# python cl.py
(, , , , , , , )
K1-foo
K2-bar
代码中的 print C.__mro__是要打印出类的继承顺序。顺序根据C括号里面的父类从左到右继承,先是J1,J1的父类K1,K2。然后到J2,最后J2的父类K3,K4。从上面清晰看出来了。如果要执行 foo() 方法,首先看 J1,没有,看 J2,还没有,看 J1 里面的 K1,有了,即 C==>J1==>J2==>K1;bar() 也是按照这个顺序,在 J2 中就找到了一个。
这种对继承属性和方法搜索的顺序称之为“广度优先”。
super 函数
对于初始化函数的继承,跟一般方法的继承,还有点不同。可以看下面的例子:
#!/usr/bin/env Python
# coding=utf-8
__metaclass__ = type
class Person:
def __init__(self):
self.height = 160
def about(self, name):
print "{} is about {}".format(name, self.height)
class Girl(Person):
def __init__(self):
self.breast = 90
def about(self, name):
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
if __name__ == "__main__":
cang = Girl()
cang.about("wangguniang")
在上面这段程序中,类 Girl 继承了类 Person。在类 Girl 中,初始化设置了 self.breast = 90,由于继承了 Person,按照前面的经验,Person 的初始化函数中的 self.height = 160 也应该被 Girl 所继承过来。然后在重写的 about 方法中,就是用 self.height。
实例化类 Girl,并执行 cang.about("wangguniang"),试图打印出一句话 wangguniang is a hot girl, she is about 160, and her bereast is 90。保存程序,运行之:
$ python 20903.py
Traceback (most recent call last):
File "20903.py", line 22, in
cang.about("wangguniang")
File "20903.py", line 18, in about
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
AttributeError: 'Girl' object has no attribute 'height'
报错!
程序员有一句名言:不求最好,但求报错。报错不是坏事,是我们长经验的时候,是在告诉我们,那么做不对。
重要的是看报错信息。就是我们要打印的那句话出问题了,报错信息显示 self.height 是不存在的。也就是说类 Girl 没有从 Person 中继承过来这个属性。
原因是什么?仔细观察类 Girl,会发现,除了刚才强调的 about 方法重写了,__init__方法,也被重写了。不要认为它的名字模样奇怪,就不把它看做类中的方法(函数),它跟类 Person 中的__init__重名了,也同样是重写了那个初始化函数。
这就提出了一个问题。因为在子类中重写了某个方法之后,父类中同样的方法被遮盖了。那么如何再把父类的该方法调出来使用呢?纵然被遮盖了,应该还是存在的,不要浪费了呀。
Python 中有这样一种方法,这种方式是被提倡的方法:super 函数。
#!/usr/bin/env python
# coding=utf-8
__metaclass__ = type
class Person:
def __init__(self):
self.height = 160
def about(self, name):
print "{} is about {}".format(name, self.height)
class Girl(Person):
def __init__(self):
super(Girl, self).__init__()
self.breast = 90
def about(self, name):
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
super(Girl, self).about(name)
if __name__ == "__main__":
cang = Girl()
cang.about("wangguniang")
在子类中,__init__方法重写了,为了调用父类同方法,使用 super(Girl, self).__init__()的方式。super 函数的参数,第一个是当前子类的类名字,第二个是 self,然后是点号,点号后面是所要调用的父类的方法。同样在子类重写的 about 方法中,也可以调用父类的 about 方法。
执行结果:
$ python 20903.py
wangguniang is a hot girl, she is about 160, and her breast is 90
wangguniang is about 160