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).