Aside: is-a versus has-a

We have used both is-a and has-a relations already but without drawing attention to the distinction between them. The is-a relationship typifies inheritance, in which we can say of each subtype or descendant class that it "is a" superclass. For instance consider the small hierarchy below,

class Person:
    ...
class Student(Person):
    ...
class Employee(Person):
    ...
class Instructor(Employee):
    ...
class Administrator(Employee):
    ...

In this case we can say that a Student is a Person and that an Employee is a Person.

Similarly we can say that an Instructor is an Employee and that an Administrator is an Employee.

Now since an Instructor is an Employee and an Employee is a Person it follows that through inheritance an Instructor is a Person and similarly that an Administrator is a Person.

Let's add some more classes and show some of the attributes.

class Date:
    ...
class CourseList:
    ...
class Person:
    def __init__(...):
        self.birthdate = ...
    ...
class Student(Person):
    def __init__(...):
        self.courses = ...
    ...
class Employee(Person):
    ...
class Instructor(Employee):
    def __init__(...):
        self.courses = ...
    ...
class Administrator(Employee):
    ...

Here we can see some has-a relationships. A Person has a birthdate which is presumably of type Date. Thus since all the classes listed below Person are also Persons they all have birthdates too and we can say everyone has a Date.

We can also see that both Students and Instructors have a courses attribute (of type CourseList, take my word for it) so we can say that a Student has a CourseList (of the ones s/he takes) and that an Instructor has a CourseList (of the ones s/he teaches).