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