Inheritance and Polymorphism

You can read this note directly if you understand classes, objects, attributes, and methods.

Inheritance and polymorphism solve two related design problems:

  • Inheritance avoids repeating features shared by related classes.
  • Polymorphism lets general code use the same method call with objects of different related classes.

Inheritance

Inheritance allows a subclass to receive attributes and methods from a superclass.

Caption: StudentUser and TeacherUser inherit shared features from User and add or override specialised features.

Suppose a system has several kinds of users. Every user has a name, but different user types need additional data and behaviour. Instead of repeating the shared code, place it in a general superclass.

class User:
    def __init__(self, name):
        self.name = name
 
    def summary(self):
        return self.name
 
 
class StudentUser(User):
    def __init__(self, name, course):
        super().__init__(name)
        self.course = course
 
    def submit_work(self):
        return f"{self.name} submitted work for {self.course}"

The declaration

class StudentUser(User):
    ...

means that StudentUser is derived from User.

A StudentUser object therefore has:

  • the inherited name attribute;
  • the inherited summary() method;
  • its own course attribute;
  • its own submit_work() method.

Tracing subclass initialisation

student = StudentUser("Aisha", "Computing")
StepCode being executedEffect
1StudentUser("Aisha", "Computing")creates a new StudentUser object
2StudentUser.__init__(...)starts initialising the subclass object
3super().__init__(name)calls User.__init__() to store the inherited name state
4self.course = coursestores the subclass-specific course state

After initialisation, student contains both inherited state and subclass-specific state.

super() is useful because it reuses the superclass implementation instead of repeating it in every subclass.

Superclass and Subclass

TermMeaning
superclassa more general class that provides features for subclasses
subclassa more specialised class that inherits from a superclass

A subclass object can use inherited methods unless the subclass replaces them.

student.summary()

Here, Python uses the inherited User.summary() method because StudentUser has not defined its own summary().

The Is-A Relationship

Inheritance should model a sensible is-a relationship:

A StudentUser is a User.
A TeacherUser is a User.

An inheritance relationship should represent specialisation, not merely a convenient way to reuse a few lines of code.

For example, a Course is not a User, so making Course inherit from User would be poor modelling even if both classes happened to store a name.

Software Reuse

Inheritance promotes software reuse because shared code is written once in the superclass and reused by subclasses.

Benefits include:

  • less duplicated code;
  • more consistent behaviour across related classes;
  • easier maintenance, because a shared change can often be made in one place;
  • improved reliability when tested superclass code is reused.

However, a change to a superclass can affect all subclasses, so the class hierarchy should still be designed carefully.

Method Overriding

A subclass can define its own version of an inherited method.

class TeacherUser(User):
    def __init__(self, name, subject):
        super().__init__(name)
        self.subject = subject
 
    def summary(self):
        return f"{self.name} teaches {self.subject}"

TeacherUser inherits from User, but its own summary() method replaces the inherited version for TeacherUser objects.

teacher = TeacherUser("Mr Tan", "Mathematics")
print(teacher.summary())

Output:

Mr Tan teaches Mathematics

This is method overriding.

IdeaMeaningSyllabus status
method overridinga subclass defines its own implementation of an inherited methodcore
method overloadingmethods share a name but use different parameter listsexcluded

For H2 Computing, focus on method overriding. Method overloading is outside the core scope.

Calling an Overridden Superclass Method

Sometimes a subclass wants to extend the superclass behaviour rather than replace it completely.

class StudentUser(User):
    def __init__(self, name, course):
        super().__init__(name)
        self.course = course
 
    def summary(self):
        basic = super().summary()
        return f"{basic} studies {self.course}"

Here, super().summary() calls the superclass implementation, and the subclass then adds more detail.

Polymorphism

Polymorphism means that the same method call can produce behaviour appropriate to the actual object.

Caption: The same summary() call works with different user object types, and Python selects the implementation belonging to the actual object.

Consider:

def get_summary(user):
    return user.summary()

The function does not contain separate branches for every class. It simply asks the object to perform summary().

users = [
    User("Alex"),
    StudentUser("Aisha", "Computing"),
    TeacherUser("Mr Tan", "Mathematics"),
]
 
for user in users:
    print(get_summary(user))

Assuming StudentUser uses the overridden summary() shown above, the calls are resolved as follows:

Actual object typeMethod usedResult
UserUser.summary()"Alex"
StudentUserStudentUser.summary()"Aisha studies Computing"
TeacherUserTeacherUser.summary()"Mr Tan teaches Mathematics"

The expression user.summary() is the same each time, but Python selects the method belonging to the actual object.

This is the key connection:

same method call + different object types → type-appropriate behaviour

Polymorphism and Code Generalisation

Without polymorphism, a programmer might write separate functions for each exact class.

def get_student_summary(student):
    ...
 
def get_teacher_summary(teacher):
    ...

With polymorphism, one general function can work with all supported object types:

def get_summary(user):
    return user.summary()

This is code generalisation. The code depends on the shared method interface rather than on each object’s internal attributes.

The objects do not need identical attributes. A StudentUser may have course, while a TeacherUser has subject. The general function only requires each object to provide a suitable summary() method.

Paper 1 and Paper 2 Emphasis

Paper 1-style explanation

Be ready to explain:

  • how inheritance promotes software reuse;
  • the difference between a superclass and a subclass;
  • what method overriding means;
  • how polymorphism supports code generalisation.

A strong explanation links the concept to its consequence. For example:

Polymorphism allows one general section of code to call the same method on objects of different classes. Each object uses the appropriate implementation, so separate code for every class is not required.

Paper 2-style coding

Be ready to:

  • declare a subclass using class Subclass(Superclass):;
  • call a superclass method or initialiser using super();
  • add subclass-specific attributes;
  • override a method;
  • trace which method is called for each object.

Syllabus Boundary

Core:

  • inheritance;
  • superclass and subclass relationships;
  • software reuse;
  • method overriding;
  • polymorphism;
  • code generalisation;
  • use of super() in simple Python class hierarchies.

Excluded:

  • method overloading;
  • multiple inheritance.

Multiple inheritance means inheriting from more than one superclass. It is not required H2 Computing 9569 content.

Common Mistakes

  • Saying that inheritance means copying and pasting code.
  • Reversing the superclass and subclass relationship.
  • Using inheritance when there is no sensible is-a relationship.
  • Repeating superclass initialisation instead of using super() where appropriate.
  • Assuming super().__init__(...) automatically initialises subclass-specific attributes.
  • Confusing method overriding with method overloading.
  • Thinking polymorphism requires all classes to have identical attributes.
  • Looking only at the variable name rather than the actual object type when deciding which overridden method runs.

Check Your Understanding

  1. What is the difference between a superclass and a subclass?
  2. Why is super().__init__(name) used in a subclass initialiser?
  3. What does method overriding mean?
  4. Why does polymorphism support code generalisation?
  5. Which method runs when an object calls a method that its subclass has overridden?

Answers:

  1. A superclass is a general class that provides inherited features; a subclass is a specialised class derived from it.
  2. It reuses the superclass initialisation code to set up inherited state.
  3. A subclass defines its own implementation of an inherited method.
  4. One general section of code can use the same method call with different object types.
  5. The subclass version runs for an object of that subclass.