This lesson is still being designed and assembled (Pre-Alpha version)

Polymorphism

Overview

Teaching: 0 min
Exercises: 0 min
Questions
  • What is Polymorphism?

Objectives
  • Understand the concepts behind Polymorphism.

Polymorphism

Polymorphism the concept of using different classes in place of one another. Specifically, an object is polymorphic if it can be used in place of one or more classes or interfaces. The intended use of a polymorphic object is to allow objects that are children of a parent class to be used as their parent class or for multiple objects that inherit from an interface to be used as the interface.

This is very similar to python’s concept of Duck Typing, i.e. if it looks like a duck and quacks like a duck, then it must be a duck. Duck typing is more of a passive concept, we assume objects of certain types can be used in certain ways and simply try to use them. For example, if we believe a variable is a number, we assume we can perform mathematical operations with it.

Polymorphism is the practice of making sure duck typing will work.

We will use the three classes defined in the Inheritance lessons, Person, Faculty, and Student to create an example of this behaviour.

First we will restate the three classes here. First the parent class Person.

class Person:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname
        self.id = self.generate_id()
    
    def generate_id(self):
        id_hash = 0
        for s in self.name:
            id_hash += ord(s)
        for s in self.surname:
            id_hash *= ord(s)
        return id_hash % 1000000000
    
    def __str__(self):
        return f'{self.surname}, {self.name}\tID: {self.id}'

Then the two child classes, Faculty and Student.

class Faculty(Person):
    def __init__(self, name, surname, position, salary):
        self.position = position
        self.salary = salary
        self.courses = []
        super().__init__(name, surname)

    def __str__(self):
        return super().__str__() + f'\nCourses:\n{self.courses}'

    def assign_course(self, new_course):
        self.courses.append(new_course)

    def unassign_course(self, course):
        self.courses.remove(course)

class Student(Person):
    def __init__(self, name, surname):
        self.courses = []
        super().__init__(name, surname)
    
    def __str__(self):
        return super().__str__() + f'\nCourses:\n{self.courses}'
        
    def enroll(self, new_course):
        self.courses.append(new_course)
        
    def drop_course(self, course):
        self.courses.remove(course)

Since Faculty and Student are both children of the Person class, take note that they both share a name, surname, and a id with their parent. To ensure that both children are polymorphic, we want to ensure that any method that operates on a Person will correctly operate on them as well.

Here we will build a simple method that utilizes variables of a Person to provide a formatted output.

def formatted_person_print(person):
    print(f'{person.id}\n{person.name} {person.surname}')

We will create a person use them in our method.

person_example = Person('John', 'Smith')
print(person_example)
Smith, John     ID: 546320160

Using out new method on this person we get our new format.

formatted_person_print(person_example)
546320160
John Smith

For Faculty and Student to be polymorphic, they should both work with this method.

faculty_example = Faculty('Jane', 'Doe', 'Department Chair', 160000)
student_example = Student('James', 'Smith')
formatted_person_print(faculty_example)
formatted_person_print(student_example)
291216936
Jane Doe
167856640
James Smith

We have properly made the two children polymorphic. Any method that utilizes Person objects should be able to use objects of either Faculty or Student. This allows us to extend the behaviour of a Person without breaking any code that relies on it.

Key Points

  • Encapsulation

  • Inheritance