OOP: Inheritance

Inheritance lets a new class reuse and extend the attributes and methods of an existing class, instead of rewriting everything from scratch. The existing class is called the parent (or base/super) class, and the new class built on top of it is called the child (or derived/sub) class.

A Child Class Inheriting from a Parent Class

Building on the Car class from the previous page, an ElectricCar is still a car, but with an extra detail: a battery range. Instead of duplicating the whole Car class, ElectricCar can simply inherit from it by putting the parent class name in parentheses:

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    def describe(self):
        return f"This car is a {self.make} {self.model}."


class ElectricCar(Car):
    pass   # inherits everything from Car, for now


my_ev = ElectricCar("Tesla", "Model 3")
print(my_ev.describe())   # This car is a Tesla Model 3.

Calling the Parent's Constructor with super().__init__()

When the child class needs its own additional attributes, it defines its own __init__, but it should still let the parent class set up the attributes it already knows about. super() gives access to the parent class from inside the child, so you don't have to repeat the parent's setup logic.

class ElectricCar(Car):
    def __init__(self, make, model, battery_range):
        super().__init__(make, model)   # let Car set up make and model
        self.battery_range = battery_range   # new attribute, specific to ElectricCar


my_ev = ElectricCar("Tesla", "Model 3", 358)
print(my_ev.make, my_ev.battery_range)   # Tesla 358

Method Overriding

A child class can redefine (override) a method it inherited, giving it completely different behavior for that subclass while everything else stays the same.

class ElectricCar(Car):
    def __init__(self, make, model, battery_range):
        super().__init__(make, model)
        self.battery_range = battery_range

    def describe(self):
        # completely replaces Car's describe() for ElectricCar objects
        return f"This electric car is a {self.make} {self.model}."


my_ev = ElectricCar("Tesla", "Model 3", 358)
print(my_ev.describe())   # This electric car is a Tesla Model 3.

Extending a Parent Method with super()

Sometimes you don't want to fully replace the parent's method — you want to reuse it and add something extra. Calling super().method_name() inside the override runs the parent's version first, and you build on its result.

class ElectricCar(Car):
    def __init__(self, make, model, battery_range):
        super().__init__(make, model)
        self.battery_range = battery_range

    def describe(self):
        base_description = super().describe()   # reuse Car's describe()
        return f"{base_description} It has a range of {self.battery_range} miles."


my_ev = ElectricCar("Tesla", "Model 3", 358)
print(my_ev.describe())
# This car is a Tesla Model 3. It has a range of 358 miles.

Multiple Inheritance

Python also allows a class to inherit from more than one parent class at once, by listing them all in parentheses:

class C(A, B):
    pass   # C inherits from both A and B

This is a powerful feature, but it can get complicated when parent classes define overlapping methods, since Python has to decide which parent's version wins (governed by something called the Method Resolution Order, or MRO). For most everyday code, single inheritance — one clear parent class — keeps things simpler and easier to reason about.

Checking Class Relationships: isinstance() and issubclass()

isinstance(object, Class) checks whether an object belongs to a class (or any of its parent classes). issubclass(ChildClass, ParentClass) checks whether one class inherits from another.

my_ev = ElectricCar("Tesla", "Model 3", 358)

print(isinstance(my_ev, ElectricCar))   # True
print(isinstance(my_ev, Car))           # True -- an ElectricCar is also a Car
print(issubclass(ElectricCar, Car))     # True
print(issubclass(Car, ElectricCar))     # False -- not every Car is an ElectricCar

Full Worked Example

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    def describe(self):
        return f"This car is a {self.make} {self.model}."


class ElectricCar(Car):
    def __init__(self, make, model, battery_range):
        super().__init__(make, model)
        self.battery_range = battery_range

    def describe(self):
        base_description = super().describe()
        return f"{base_description} It has a range of {self.battery_range} miles."


my_car = Car("Toyota", "Corolla")
my_ev = ElectricCar("Tesla", "Model 3", 358)

print(my_car.describe())   # This car is a Toyota Corolla.
print(my_ev.describe())    # This car is a Tesla Model 3. It has a range of 358 miles.
print(isinstance(my_ev, Car))   # True
Next steps: Head to Iterators & Generators to learn how Python's for loops actually work under the hood.