SyntaxStudy
Sign Up
Python Composition vs Inheritance
Python Intermediate 10 min read

Composition vs Inheritance

Composition vs Inheritance

"Favor composition over inheritance." Inheritance is an is-a relationship; composition is a has-a relationship. Composition is more flexible and easier to test.

Inheritance (is-a)

class Animal:
    def breathe(self): return "breathing"

class Dog(Animal):    # Dog IS AN Animal
    def bark(self):   return "Woof!"

# Works, but creates tight coupling.
# Changes to Animal affect all subclasses.

Composition (has-a)

class Engine:
    def __init__(self, horsepower):
        self.hp = horsepower
    def start(self):
        return f"Engine started ({self.hp}hp)"

class GPS:
    def navigate(self, dest):
        return f"Navigating to {dest}"

class Car:           # Car HAS AN Engine and a GPS
    def __init__(self, make, hp):
        self.make   = make
        self.engine = Engine(hp)
        self.gps    = GPS()

    def start(self):
        return f"{self.make}: {self.engine.start()}"

    def navigate(self, dest):
        return self.gps.navigate(dest)

car = Car("Toyota", 150)
print(car.start())
print(car.navigate("Airport"))

Combining Both

class Logger:
    def log(self, msg): print(f"[LOG] {msg}")

class UserService:
    def __init__(self):
        self.logger = Logger()   # composition

    def create_user(self, name):
        self.logger.log(f"Creating user: {name}")
        return {"name": name, "id": id(name)}
Example
class Battery:
    def __init__(self, capacity_kwh):
        self.capacity = capacity_kwh
        self.charge = capacity_kwh

    def drain(self, amount):
        self.charge = max(0, self.charge - amount)
        return self.charge

class Motor:
    def __init__(self, power_kw):
        self.power = power_kw
    def run(self, duration):
        return self.power * duration  # energy used

class ElectricBike:   # HAS a Battery and a Motor
    def __init__(self):
        self.battery = Battery(0.5)
        self.motor   = Motor(0.25)

    def ride(self, hours):
        used = self.motor.run(hours)
        remaining = self.battery.drain(used)
        print(f"Rode {hours}h, battery at {remaining:.2f}kWh")

bike = ElectricBike()
bike.ride(1)
bike.ride(1)
Pro Tip

Use inheritance when there is a genuine is-a relationship and shared behavior. Use composition when you want flexibility — you can swap components (e.g., swap Battery for a bigger one) without changing the class hierarchy.