I remember when I first started architecting large-scale Python applications for a startup. I often found myself struggling to decide whether a class should inherit from another or simply contain it.
It is a classic debate that every seasoned developer faces: Composition vs. Inheritance.
In this tutorial, I will share my firsthand experience to help you understand these concepts using real-world scenarios.
What is Inheritance in Python?
Inheritance is a way to create a new class that is a modified version of an existing class.
In my early years of development, I used inheritance constantly because it felt like the most “natural” way to reuse code.
It represents an “is-a” relationship. For example, a TeslaModel3 “is-a” ElectricVehicle.
When you use inheritance, the child class automatically gains all the attributes and methods of the parent class.
What is Composition in Python?
Composition is a design principle where a class is composed of one or more objects of other classes.
Instead of inheriting behavior, the class “has” an object that provides the behavior.
It represents a “has-a” relationship. For example, a FordF150 “has-a” V8Engine.
I’ve found that as systems grow more complex, composition often provides a level of flexibility that inheritance cannot match.
Method 1: Use Inheritance for Hierarchical Data
I typically use inheritance when I am dealing with a clear hierarchy where the child truly is a specialized version of the parent.
Suppose we are building a payroll system for a US-based logistics company. We have a general Employee class and specialized types like FullTimeEmployee.
Here is the full code example:
# Base Class
class Employee:
def __init__(self, name, employee_id):
self.name = name
self.employee_id = employee_id
def display_info(self):
print(f"ID: {self.employee_id} | Name: {self.name}")
# Child Class inheriting from Employee
class FullTimeEmployee(Employee):
def __init__(self, name, employee_id, annual_salary):
# Inheriting attributes from the parent
super().__init__(name, employee_id)
self.annual_salary = annual_salary
def calculate_monthly_pay(self):
return self.annual_salary / 12
# Real-world usage in a US company context
emp1 = FullTimeEmployee("James Miller", "TX-9921", 120000)
emp1.display_info()
print(f"Monthly Pay: ${emp1.calculate_monthly_pay():,.2f}")I executed the above example code and added the screenshot below.

In this case, a FullTimeEmployee is an Employee. It makes sense to inherit the ID and name attributes.
Method 2: Use Composition for Component-Based Design
I often turn to composition when I want to build objects using “plug-and-play” components.
Think of a modern American smart home system. A SmartHome isn’t a “type” of SecurityCamera; rather, it has a camera.
Using composition allows me to swap components easily without breaking the entire class structure.
class SecurityCamera:
def __init__(self, brand, resolution):
self.brand = brand
self.resolution = resolution
def record(self):
return f"Recording 4K video with {self.brand} camera..."
class SmartLock:
def __init__(self, status="Locked"):
self.status = status
def toggle(self):
self.status = "Unlocked" if self.status == "Locked" else "Locked"
return f"Door is now {self.status}"
# Composed Class
class SmartHomeSystem:
def __init__(self, street_address):
self.address = street_address
# Composition: Including objects of other classes
self.camera = SecurityCamera(brand="Nest", resolution="4K")
self.lock = SmartLock()
def secure_home(self):
print(f"Securing home at {self.address}")
print(self.camera.record())
print(self.lock.toggle())
# Usage
my_home = SmartHomeSystem("1600 Pennsylvania Ave NW, Washington, DC")
my_home.secure_home()I executed the above example code and added the screenshot below.

By using composition here, I can add a SmartThermostat later without changing the base logic of what a “home” is.
Method 3: Combine Both (The Hybrid Approach)
I have found that the best architectures often use a mix of both. Let’s look at a US-based e-commerce shipping example like FedEx or UPS.
A Shipment might be a base class, but it contains a ShippingAddress object (composition).
class ShippingAddress:
def __init__(self, city, state, zip_code):
self.city = city
self.state = state
self.zip_code = zip_code
def get_full_address(self):
return f"{self.city}, {self.state} {self.zip_code}"
class Shipment:
def __init__(self, tracking_number, destination):
self.tracking_number = tracking_number
self.destination = destination # Composition
class OvernightShipment(Shipment): # Inheritance
def __init__(self, tracking_number, destination, rush_fee):
super().__init__(tracking_number, destination)
self.rush_fee = rush_fee
def print_label(self):
print(f"Tracking: {self.tracking_number}")
print(f"Shipping to: {self.destination.get_full_address()}")
print(f"Priority: Overnight (Fee: ${self.rush_fee})")
# Usage
dest = ShippingAddress("Chicago", "IL", "60601")
package = OvernightShipment("1Z999AA1", dest, 25.50)
package.print_label()I executed the above example code and added the screenshot below.

Why I Prefer Composition Over Inheritance
Many developers follow the mantra “Favor Composition over Inheritance.”
I agree with this because inheritance can lead to a “Deep Hierarchy” problem.
If you change a base class at the top of a large hierarchy, you risk breaking dozens of child classes.
Composition is more loosely coupled. You can change the SecurityCamera class without ever touching the SmartHomeSystem logic.
When to Choose Inheritance
Despite the trend toward composition, inheritance still has its place in my toolkit.
I use it when I am implementing an interface or a framework-specific class.
For instance, if you are using a Python web framework like Django, you must inherit from models.Model.
It is also great for reducing boilerplate code when classes share 90% of the same logic.
Key Differences at a Glance
| Feature | Inheritance | Composition |
| Relationship | “Is-a” | “Has-a” |
| Coupling | Tightly Coupled | Loosely Coupled |
| Flexibility | Static (Fixed at compile time) | Dynamic (Can change at runtime) |
| Code Reuse | Reuses implementation | Reuses behavior |
Performance Considerations in Python
In my experience, the performance difference between the two is negligible for 99% of applications.
Python’s Method Resolution Order (MRO) handles inheritance very efficiently.
However, composition creates more objects in memory, which could be a factor if you are dealing with millions of instances.
For most US-based enterprise applications, I suggest focusing on code maintainability over micro-optimizations.
Common Issues to Avoid
One mistake I see junior developers make is “Inclusion Inheritance.”
This is when you inherit from a class to get access to its methods, even if the “is-a” relationship doesn’t exist.
For example, inheriting TaxCalculator into a User class is a bad practice.
The User isn’t a tax calculator; the User should use a tax calculator via composition.
Another pitfall is “The Diamond Problem” in multiple inheritance, which can make your code very hard to debug.
My Personal Recommendation
If you are unsure which one to use, start with composition.
It is much easier to refactor composition into inheritance later than it is to untangle a messy inheritance tree.
Think about how components interact. If you are building something modular, composition is your best friend.
If you are building a strict classification system, inheritance will serve you well.
I hope this guide helps you make better architectural decisions in your Python projects.
Whether you are building a small script or a large-scale system for a US tech firm, these principles remain the same.
You may also read:
- Python @property vs Normal Methods
- How to Use @property Decorator in Python
- How to Use Abstract Base Classes (ABC) in Python
- Python Abstract Class vs Concrete Class

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.