No history yet

Inheritance and Polymorphism

Passing Down Traits

In programming, we often find that different types of objects share common features. A car is a type of vehicle. A dog is a type of animal. A savings account is a type of bank account. Object-oriented programming gives us a powerful way to model these relationships using a concept called inheritance.

Inheritance allows a new class to absorb the properties and methods of an existing class. This promotes code reuse, so you don't have to write the same logic over and over. The original class is called the base class (or parent class), and the new class that inherits from it is called the derived class (or child class).

Think of it like genetic inheritance. Children inherit traits like eye color and height from their parents, but they also have their own unique characteristics.

Let's see this with a quick example. Imagine we have a Vehicle class.

class Vehicle:
    def __init__(self, color):
        self.color = color

    def start_engine(self):
        print("Engine starting...")

# A Car is a type of Vehicle, so it inherits from it.
class Car(Vehicle):
    def honk(self):
        print("Beep beep!")

my_car = Car("red")
my_car.start_engine() # Inherited from Vehicle
my_car.honk()         # Defined in Car
print(my_car.color)   # Attribute inherited from Vehicle

Changing Inherited Behavior

A derived class isn't stuck with the exact behavior of its parent. It can provide its own specific version of a method that it inherited. This is called method overriding.

For example, a Car might start its engine differently than a generic Vehicle. The Car class can override the start_engine method to make it more specific.

class Vehicle:
    def start_engine(self):
        print("Engine starting...")

class Car(Vehicle):
    # Overriding the parent's method
    def start_engine(self):
        print("Vroom! Car engine starting.")

my_car = Car()
my_car.start_engine() # Calls the Car's version

But what if you want to add to the parent's behavior instead of completely replacing it? You can use the super() keyword to call the parent class's method from within the child's method.

class Vehicle:
    def __init__(self, color):
        self.color = color

    def start_engine(self):
        print("Engine starting...")

class Car(Vehicle):
    def __init__(self, color, num_doors):
        # Call the parent's __init__ method
        super().__init__(color)
        self.num_doors = num_doors

    def start_engine(self):
        # Call the parent's method first
        super().start_engine()
        print("Vroom! Car engine revving.")

my_car = Car("blue", 4)
my_car.start_engine()

One Name, Many Forms

Now we get to polymorphism, a concept that sounds complex but is beautifully simple. The word comes from Greek and means "many forms." In programming, it means we can treat objects of different classes as if they were objects of a common parent class. It allows us to write more flexible and generic code.

Because both a Car and a Motorcycle inherit from Vehicle and have their own start_engine methods, we can tell any vehicle to start its engine without needing to know exactly what kind of vehicle it is. The program figures out the right method to call at runtime.

class Motorcycle(Vehicle):
    def start_engine(self):
        super().start_engine()
        print("Rumble! Motorcycle engine on.")

# Create a list of different Vehicle objects
vehicles = [Car("red", 4), Motorcycle("black")]

# We can treat them all as Vehicles
for vehicle in vehicles:
    vehicle.start_engine()
    print("---")

This ability to decide which method to run at the moment of execution is called dynamic binding. It’s the magic that makes polymorphism work with method overriding. It’s also known as runtime polymorphism.

Another type of polymorphism is method overloading. This is when a single class has multiple methods with the same name but different numbers or types of parameters. The correct method to call is determined at compile time based on the arguments you provide. Note: Not all languages, like Python, support traditional method overloading in this way, but the concept is central to object-oriented programming.

For example, a Calculator class might have an add method that can sum two numbers or three numbers, depending on how many are given.

FeatureMethod OverridingMethod Overloading
PurposeProvide a specific implementation of a method that is already defined in a parent class.Have multiple methods with the same name but different parameters in the same class.
RelationshipOccurs between a parent class and a child class.Occurs within a single class.
ParametersMust have the same method name and parameters as the parent's method.Must have the same method name but different parameters (number or type).
PolymorphismExample of Runtime Polymorphism (Dynamic Binding).Example of Compile-time Polymorphism (Static Binding).

Inheritance and polymorphism are cornerstone concepts that help you write code that is clean, reusable, and easy to extend.

Ready to test your knowledge?

Quiz Questions 1/6

In the context of inheritance, what is the relationship between a base class and a derived class?

Quiz Questions 2/6

What is the primary purpose of method overriding?

By building hierarchies of classes and enabling objects to take many forms, you can model complex systems in an intuitive and powerful way.