Python Composition vs Inheritance

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.

Python Composition vs Inheritance

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.

Difference between Composition and Inheritance in Python

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.

Python Difference between Composition and Inheritance

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

FeatureInheritanceComposition
Relationship“Is-a”“Has-a”
CouplingTightly CoupledLoosely Coupled
FlexibilityStatic (Fixed at compile time)Dynamic (Can change at runtime)
Code ReuseReuses implementationReuses 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:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.