12: Single Inheritance¶
Summary¶
Inheritance is at its most basic about code reuse
- OOP has a number of different philosophies that attempt to motivate its use.
The most common is that classes should model the real world - the domain model
The domain model has to be hierarchial because single inheritance creates class hierarchies; the problem is that the world isn’t a simple hierarchy
The four pillars of OOP are classes, inheritance, encapsulation, and polymorphism
Python allows multiple inheritance, but you can restrict your use of it to single inheritance if you want to
- Inheritance in Python is a simple extension of the way attributes are resolved
First the instance class is created, then the class, then the base class, and then its base class, and so on until the attribute is located
Attributes can be overridden by simplify defining them in the class that inherits
The super() built-in function can be used to return an attribute from the first class in the inheritance chain to define it
The inheritance chain is defined in the class’s __mro__ attribute
- super() works by searching the __mro__ for the first class to define the method
You can specify the class that has the __mro__ to use and the starting class for the search
If you restrict yourself to single inheritance you can dispense with super() and access the attribute directly, but with multiple inheritance this simple approach doesn’t work
Metaclasses can also be a part of an inheritance chain and there are rules for which metaclass is used.
Program¶
"""
Inheritance Demonstration Program
This program illustrates key Object-Oriented Programming (OOP) concepts:
- Inheritance as code reuse
- Domain modeling with classes (Vehicle → Car → ElectricCar)
- Encapsulation (attributes inside classes)
- Polymorphism (method overriding)
- Attribute resolution order (__mro__)
- Use of super() for method delegation
- Single inheritance (with notes on multiple inheritance)
The goal is to show how Python resolves attributes and how inheritance chains work.
"""
# -------------------------------
# BASE CLASS (Domain Model Root)
# -------------------------------
class Vehicle:
"""
A base class representing a general vehicle.
"""
def __init__(self, brand):
# Encapsulation: storing data inside the object
self.brand = brand
def start(self):
"""
Basic method that can be reused or overridden.
"""
return f"{self.brand} vehicle is starting..."
def info(self):
"""
General information method.
"""
return f"This is a vehicle made by {self.brand}"
# -------------------------------
# INTERMEDIATE CLASS
# -------------------------------
class Car(Vehicle):
"""
Car inherits from Vehicle.
Demonstrates single inheritance and method extension.
"""
def __init__(self, brand, doors):
# Using super() to call parent constructor
super().__init__(brand)
self.doors = doors
def info(self):
"""
Method overriding (polymorphism).
Extends parent behavior using super().
"""
base_info = super().info() # Calls Vehicle.info()
return f"{base_info} with {self.doors} doors"
def start(self):
"""
Overriding method completely.
"""
return f"{self.brand} car engine starts with a key."
# -------------------------------
# FINAL CHILD CLASS
# -------------------------------
class ElectricCar(Car):
"""
ElectricCar inherits from Car.
Demonstrates deeper inheritance chains.
"""
def __init__(self, brand, doors, battery_capacity):
super().__init__(brand, doors)
self.battery_capacity = battery_capacity
def start(self):
"""
Overriding again to demonstrate polymorphism.
"""
return f"{self.brand} electric car starts silently."
def battery_info(self):
return f"Battery capacity: {self.battery_capacity} kWh"
# -------------------------------
# MAIN EXECUTION
# -------------------------------
if __name__ == "__main__":
# Create an instance of ElectricCar
tesla = ElectricCar("Tesla", 4, 75)
# Demonstrate attribute resolution and method calls
print("START METHOD:")
print(tesla.start()) # Uses ElectricCar.start()
print("\nINFO METHOD:")
print(tesla.info()) # Uses Car.info() → super() → Vehicle.info()
print("\nBATTERY INFO:")
print(tesla.battery_info())
# -------------------------------
# ATTRIBUTE RESOLUTION ORDER (MRO)
# -------------------------------
print("\nMETHOD RESOLUTION ORDER (__mro__):")
for cls in ElectricCar.__mro__:
print(cls)
"""
Output shows how Python searches for attributes:
1. ElectricCar
2. Car
3. Vehicle
4. object
This is the inheritance chain used when resolving methods/attributes.
"""
Program Output¶
START METHOD:
Tesla electric car starts silently.
INFO METHOD:
This is a vehicle made by Tesla with 4 doors
BATTERY INFO:
Battery capacity: 75 kWh
METHOD RESOLUTION ORDER (__mro__):
<class '__main__.ElectricCar'>
<class '__main__.Car'>
<class '__main__.Vehicle'>
<class 'object'>