子类除了继承超类的属性,还包含一些自己的特殊属性。例如,“研究生”具有导师信 息,而一般的“学生”未必有导师。在进行面向对象设计时,一般先定义超类,然后在超类 基础上通过添加一些特殊属性来定义子类。这种定义方式下,子类中不必重复定义那些继承 来的属性,从而简化了子类定义。这也是继承机制带来了的另一个重要特色——代码重用(code reuse),即超类中的代码可以通过继承机制被子类重复使用。当我们需要定义一个新 类时,如果发现它与某个现有的类在很多方面都相同,那么就无需重新写代码来实现这些相 同行为,而只需继承现有功能。

    注意,超类与子类的定义一般置于同一个模块中。如果超类在另一个模块中定义,则定义子 类时必须指明模块信息,形如:

    1. ...

    下面通过具体例子来说明超类、子类以及继承概念。程序 7.3 中定义了一个 Person 类, 它在最一般的层次上刻划了“人”对象:每个人有姓名和出生年份数据,并且能回答外界提 出的“叫什么名字”、“今年多大了”之类的问题。为便于阅读、比较,我们将 Person 类的定 义复制于此:

    1. class Student(Person):
    2. def __init__ (self,n,y,u,id):
    3. Person.__init__ (self,n,y)
    4. self.univ = u
    5. self.snum = id
    6. def getUniv(self):
    7. return self.univ
    8. def getNum(self):

    Student 类定义的第一行表明,Student 类是 Person 类的子类。作为特殊的人,学生拥有 普通人不一定有的 self.univ(学校)和 self.snum(学号)数据,因此 Student 类的构造器与 Person 不同,需要四个初始参数:姓名、出生年份、学校和学号。创建 Student 对象时要进 行的初始化工作包括:首先作为 Person 对象要执行 Person 对象的初始化,即 Person.__init (); 然后再执行 Student 对象特有的初始化工作,即对 self.univ 和 self.snum 两个实例变量进行赋 值。可见,子类的构造器一般是在超类构造器的基础上另外执行一些初始化工作。Student 对象除了能响应 Person 对象都能响应的 whatName 和 howOld 之外,还具有两个特有的方法: getUniv 和 getNum。 我们再来定义另一种特殊的人——教师。假设教师拥有指导的学生人数信息,以及设置和获取这个信息的方法,则可定义 Teacher 类如下:

    Teacher 类没有定义自己的初始化方法 init,因此创建 Teacher 对象时将自动调用超类 Person 的 init方法来进行初始化。Teacher 对象有一个特殊的实例变量 num,但其值不是 在创建对象时初始化的,而是在创建之后利用 setNum 方法来设置。不要忘了,Python 对象 的实例变量可以在任何方法中定义,可以在任何时候赋值。

    1. >>> tom = Student("Tom",1995,"SJTU","S001")
    2. >>> tom.whatName()
    3. My name is Tom
    4. >>> tom.howOld(2013)
    5. My age in 2013 is 18
    6. >>> tom.getUniv()
    7. 'SJTU'
    8. >>> print tom.getNum()
    9. S001
    10. My name is Huck
    11. >>> huck.howOld(2013)
    12. My age in 2013 is 38
    13. >>> huck.setNum(8)
    14. >>> print huck.getNum()
    15. 8
    16. >>> lucy = Person("Lucy",2005)
    17. >>> lucy.getUniv()
    18. Traceback (most recent call last):

    子类继承超类的所有属性,因此当创建了 Student 对象 tom 后,就可以向 tom 发消息 whatName 和 howOld,tom 对象能够正确地响应这两个消息。当然还可以向 tom 发送 getUniv 和 getNum 消息,这两个方法是 Student 特有的,tom 自然能做出响应。Teacher 对象 huck 的行为也是类似的。注意,超类的实例并不具有子类中特殊属性,因此上例中向 Person 对象 lucy发送 getUniv 消息,将导致错误。