Python OOP Polymorphism Tutorial

In this tutorial we learn how to override parent class members inside a child class. We cover how to overload dunder methods, and also look at a list of common magic methods.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is polymorphism?

Polymorphism is a big scary word that comes from the Greek “poly” meaning many, and “morphe” meaning form. It means that something can have many forms.

In the case of Object Oriented Programming, it means that an object can be treated as having many different types. Polymorphism enables a child class to define its own version of a member that’s also defined by its parent class.

In simple terms, polymorphism is when we override a built-in (magic) method.

If you come from a language such as C#, magic methods would be similar to operator overloading.

Polymorphism works with magic methods (dunder methods). The methods that are prefixed and postfixed with double underscores.

An example of a dunder method would be the __init__() method. Another example would be the + operator, which is actually shorthand for a dunder method called __add__(). The __add__() method uses polymorphic behaviour.

If the operands are instances of ints, the method will do a mathematical addition. If the operands are strings, it concatenates the strings together.

Example:
num = 1 + 3		 # 4 (int)
str = "1" + "3"  # 13 (string)

print("num = ", num, type(num))
print("str = ", str, type(str))

In the example above, we see polymorphism in action. The + operator is doing both arithmetic and concatenation.

Operator overloading

To override a dunder method we simply define a method with the same name as the method we want to override.

As an example, let’s consider the + operator. We know that its dunder method is __add__() so we can define a method called __add__() in our own class and make it do what we want.

Then when we use the + operator it will do what we specified in our method, instead of the default behavior.

Example:
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # overwrite the __repr__ dunder
    def __repr__(self):
        return f"Dog named {self.name}, {self.age} years old"

    # overwrite the + operator
    def __add__(self, other):
        if isinstance(other, Dog):
            return Dog(name="Newborn", age=0)
        else:
            return "You can't breed a dog with that!"

daddy = Dog("Clyde", 3)
mommy = Dog("Bonnie", 3)

puppy = mommy + daddy
print(puppy)

In the example above, we overrode the __add__() method to add two dogs together.

When we use the + operator on the mommy and daddy objects, the interpreter executes the __add__() method in the Dog class and produces a new Dog instance into the puppy variable, called “Newborn”.

List of common magic (dunder) methods

The following tables list common magic, or dunder, methods.

Initialization and Construction:
NameDescription
__new__()Called in an object's instantiation.
__init__()Called by the __new__ method.
__del__()Destructor method.
Unary operators and functions:
NameDescription
__abs__()Called by built-in abs() function.
__round__()Called by built-in round() function.
__floor__()Called by built-in math.floor() function.
__ceil__()Called by built-in math.ceil() function.
__trunc__()Called by built-in math.trunc() function.
Type Conversion
NameDescription
__int__()Called by built-in int() method to convert to an int.
__float__()Called by built-in float() method to convert to float.
Strings
NameDescription
__str__()Called by built-in str() method to return a string representation of a type.
__unicode__()Called by built-in unicode() method to return an unicode string of a type.
__format__()Called by built-in string.format() method to return a new style of string.
__dir__()Called by built-in dir() method to return a list of attributes of a class.
__sizeof__()Called by built-in sys.getsizeof() method to return the size of an object.
Attribute
NameDescription
__getattr__()Called when the accessing a class attribute that does not exist.
__setattr__()Called when assigning a value to a class attribute.
__delattr__()Called when deleting a class attribute.
Operator
NameDescription
__add__()Called on addition operation.
__sub__()Called on subtraction operation.
__mul__()Called on multiplication operation.
__div__()Called on division operation.
__mod__()Called on modulo operation.
__pow__()Called on calculating the power.
Augmented assignment
NameDescription
__iadd__()Called on addition with assignment.
__isub__()Called on subtraction with assignment.
__imul__()Called on multiplication with assignment.
__idiv__()Called on division with assignment.
__imod__()Called on modulo with assignment.
__ipow__()Called on exponents with assignment.

Summary: Points to remember

  • Polymorphism is when a child class defines its own version of a method in a parent class.
  • Polymorphism in Python uses magic methods, otherwise known as dunder methods.
  • To override a magic method we create a method with the exact same name.