Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • šŸ“ Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • šŸ“ Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • šŸ“ Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • šŸ“ Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • šŸ“ While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • šŸ“ For Each Assignments
    • šŸ“ All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • šŸ“ Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • šŸ“ Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • šŸ“ Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • šŸ“ Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • šŸ“ Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • šŸ“ Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • šŸ“ Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • šŸ“ Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • šŸ“ Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • šŸ“ Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • šŸ“ Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • šŸ“ Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • šŸ“ Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • šŸ“ Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • šŸ“ Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • šŸ“ Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • šŸ“ Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • šŸ“ Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • šŸ“ Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Inheritance vs Composition

Python Inheritance vs Composition - Complete Guide

Learn when to use inheritance and when to use composition.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What's the Difference?
  • Inheritance — IS-A Relationship
  • Composition — HAS-A Relationship
  • Key Differences
  • When to Use What
  • Real-World Example
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • Inheritance — IS-A relationship, sharing code through parent classes
  • Composition — HAS-A relationship, building complex objects from simpler ones
  • Key differences — when to use each approach
  • Pros and cons — flexibility, reusability, and maintenance
  • Real-world examples — see both approaches in action

What's the Difference?

When you're designing classes in Python, you have two main ways to reuse code and build relationships between objects: inheritance and composition.

Here's the quick version:

  • Inheritance is a "IS-A" relationship. A Dog IS-A Animal. It inherits everything from the parent class.
  • Composition is a "HAS-A" relationship. A Car HAS-A Engine. It contains other objects as parts.

The choice between inheritance and composition is one of the most important design decisions in object-oriented programming. Get it right, and your code will be flexible and easy to maintain. Get it wrong, and you'll end up with brittle, hard-to-change code.

šŸ’” Key concept: Inheritance is about being something. Composition is about having something. A dog is an animal. A car has an engine.

Inheritance — IS-A Relationship

1

What is Inheritance?

Inheritance is when a class (child) derives from another class (parent). The child inherits all the methods and attributes of the parent and can add new ones or override existing ones.

The relationship is "IS-A". A Cat IS-A Animal. A Sedan IS-A Car.

Inheritance is great for sharing code and creating hierarchies of related classes. It's the foundation of polymorphism in many OOP languages.

# Inheritance — IS-A Relationship

print("=" * 50)
print("INHERITANCE — IS-A RELATIONSHIP")
print("=" * 50)

class Animal:
    """Base class — all animals"""
    
    def __init__(self, name):
        self.name = name
        self.is_alive = True
    
    def breathe(self):
        return f"{self.name} is breathing"
    
    def sleep(self):
        return f"{self.name} is sleeping"
    
    def eat(self):
        return f"{self.name} is eating"
    
    def make_sound(self):
        return f"{self.name} makes a sound"

class Dog(Animal):
    """Dog IS-A Animal — inherits everything and adds/overrides"""
    
    def __init__(self, name, breed):
        super().__init__(name)  # Call parent constructor
        self.breed = breed
    
    def make_sound(self):
        """Override: dogs bark"""
        return f"{self.name} says: Woof! Woof!"
    
    def wag_tail(self):
        """New method: specific to dogs"""
        return f"{self.name} is wagging tail"

class Cat(Animal):
    """Cat IS-A Animal — inherits everything and adds/overrides"""
    
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color
    
    def make_sound(self):
        """Override: cats meow"""
        return f"{self.name} says: Meow!"
    
    def purr(self):
        """New method: specific to cats"""
        return f"{self.name} is purring"

class Bird(Animal):
    """Bird IS-A Animal — inherits everything and adds/overrides"""
    
    def __init__(self, name, wingspan):
        super().__init__(name)
        self.wingspan = wingspan
    
    def make_sound(self):
        """Override: birds chirp"""
        return f"{self.name} says: Chirp! Chirp!"
    
    def fly(self):
        """New method: specific to birds"""
        return f"{self.name} is flying with wingspan {self.wingspan}cm"

# --- Demonstration ---
dog = Dog("Rex", "German Shepherd")
cat = Cat("Whiskers", "Orange")
bird = Bird("Tweety", 25)

print(" DOG (Dog IS-A Animal)")
print(f"  {dog.breathe()}")        # Inherited
print(f"  {dog.sleep()}")          # Inherited
print(f"  {dog.eat()}")            # Inherited
print(f"  {dog.make_sound()}")     # Overridden
print(f"  {dog.wag_tail()}")       # New method

print("\n🐈 CAT (Cat IS-A Animal)")
print(f"  {cat.breathe()}")        # Inherited
print(f"  {cat.make_sound()}")     # Overridden
print(f"  {cat.purr()}")           # New method

print("\n🐦 BIRD (Bird IS-A Animal)")
print(f"  {bird.breathe()}")       # Inherited
print(f"  {bird.make_sound()}")    # Overridden
print(f"  {bird.fly()}")           # New method

print("\n" + "-" * 30)
print("INHERITANCE BENEFITS:")
print("-" * 30)
print(" Code reuse: common methods in Animal are shared")
print(" Polymorphism: each animal has its own make_sound()")
print(" Extensibility: new animals can be added easily")
print(" Clear hierarchy: relationships are explicit")

print("\n INHERITANCE CHALLENGES:")
print("   • Can create deep, complex hierarchies")
print("   • Changes in parent affect all children")
print("   • Can lead to 'fragile base class' problems")

Inheritance key points:

  • IS-A relationship — a dog IS-A animal, a car IS-A vehicle
  • Code reuse — inherits all parent methods and attributes
  • Override behavior — child can change how methods work
  • Create hierarchies — organize classes in a tree structure
  • Single inheritance — Python classes can inherit from one parent (for main hierarchy)

Quick Check: What type of relationship does inheritance represent? (Answer: IS-A — a dog IS-A animal)

Composition — HAS-A Relationship

2

What is Composition?

Composition is when a class is built from other objects. Instead of inheriting behavior, a class contains instances of other classes and delegates work to them.

The relationship is "HAS-A". A Car HAS-A Engine. A Computer HAS-A Processor.

Composition is great for building complex objects from simpler parts and for decoupling code. It's more flexible than inheritance because you can change components at runtime.

# Composition — HAS-A Relationship

print("=" * 50)
print("COMPOSITION — HAS-A RELATIONSHIP")
print("=" * 50)

# ============================================================
# COMPONENT CLASSES — The "parts"
# ============================================================

class Engine:
    """Engine component — a car HAS-A engine"""
    
    def __init__(self, horsepower, fuel_type):
        self.horsepower = horsepower
        self.fuel_type = fuel_type
        self.is_running = False
    
    def start(self):
        self.is_running = True
        return f"Engine started (HP: {self.horsepower}, Fuel: {self.fuel_type})"
    
    def stop(self):
        self.is_running = False
        return "Engine stopped"
    
    def get_power(self):
        return f"Engine power: {self.horsepower} HP"

class Wheels:
    """Wheels component — a car HAS-A wheels"""
    
    def __init__(self, count=4):
        self.count = count
    
    def rotate(self):
        return f"Rotating {self.count} wheels"

class Steering:
    """Steering component — a car HAS-A steering"""
    
    def __init__(self, type="power"):
        self.type = type
    
    def turn(self, direction):
        return f"Turning {direction} with {self.type} steering"

class Transmission:
    """Transmission component — a car HAS-A transmission"""
    
    def __init__(self, type="automatic"):
        self.type = type
        self.gear = 0
    
    def shift(self, gear):
        self.gear = gear
        return f"Shifted to gear {gear} ({self.type})"

class Radio:
    """Radio component — a car HAS-A radio"""
    
    def __init__(self):
        self.is_on = False
        self.station = "FM 101.1"
    
    def turn_on(self):
        self.is_on = True
        return f"Radio on: {self.station}"
    
    def turn_off(self):
        self.is_on = False
        return "Radio off"

# ============================================================
# COMPOSITE CLASS — Built from components
# ============================================================

class Car:
    """Car built from components using composition"""
    
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        
        # Composition: Car HAS-A engine, wheels, steering, etc.
        self.engine = Engine(200, "gasoline")
        self.wheels = Wheels(4)
        self.steering = Steering("power")
        self.transmission = Transmission("automatic")
        self.radio = Radio()
        
        self.speed = 0
    
    def start(self):
        # Delegation: car uses its components
        engine_status = self.engine.start()
        return f"{self.make} {self.model}: {engine_status}"
    
    def stop(self):
        engine_status = self.engine.stop()
        return f"{self.make} {self.model}: {engine_status}"
    
    def drive(self, speed):
        self.speed = speed
        wheel_status = self.wheels.rotate()
        trans_status = self.transmission.shift(3)
        return f"Driving at {speed} km/h — {wheel_status}, {trans_status}"
    
    def turn(self, direction):
        return self.steering.turn(direction)
    
    def listen_radio(self):
        return self.radio.turn_on()
    
    def get_info(self):
        return f"{self.year} {self.make} {self.model}"

# ============================================================
# DEMONSTRATION
# ============================================================

car = Car("Toyota", "Camry", 2023)

print("šŸš— CAR COMPOSITION")
print(f"  {car.get_info()}")

print("\nšŸ”§ COMPONENTS:")
print(f"  Engine: {car.engine.get_power()}")
print(f"  Wheels: {car.wheels.count} wheels")
print(f"  Steering: {car.steering.type} steering")
print(f"  Transmission: {car.transmission.type}")

print("\n DRIVING THE CAR:")
print(f"  {car.start()}")
print(f"  {car.drive(60)}")
print(f"  {car.turn('left')}")
print(f"  {car.listen_radio()}")

print("\n" + "-" * 30)
print("COMPOSITION BENEFITS:")
print("-" * 30)
print("Flexible: can swap components (electric engine, different wheels)")
print(" Decoupled: components are independent")
print(" Reusable: same components can be used in different cars")
print(" Testable: components can be tested separately")
print(" Single responsibility: each component does one thing")

print("\n COMPOSITION CHALLENGES:")
print("   • More code to write (need to set up components)")
print("   • Delegation overhead (calls need to be passed through)")
print("   • May need to expose component methods")

Composition key points:

  • HAS-A relationship — a car HAS-A engine, a computer HAS-A processor
  • Build from parts — complex objects are assembled from simpler ones
  • Flexibility — components can be swapped at runtime
  • Decoupled — components don't depend on each other
  • Delegation — the containing object forwards calls to its components

Quick Check: What type of relationship does composition represent? (Answer: HAS-A — a car HAS-A engine)

Key Differences

3

Inheritance vs Composition — Side by Side

# Inheritance vs Composition — Complete Comparison

print("=" * 60)
print("INHERITANCE vs COMPOSITION")
print("=" * 60)

# ============================================================
# EXAMPLE 1: INHERITANCE APPROACH
# ============================================================

print("\n1. INHERITANCE APPROACH (IS-A)")

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    
    def work(self):
        return f"{self.name} is working"
    
    def get_pay(self):
        return f"Paid ${self.salary}"

class Manager(Employee):
    def __init__(self, name, salary, team_size):
        super().__init__(name, salary)
        self.team_size = team_size
    
    def work(self):
        return f"{self.name} is managing {self.team_size} people"
    
    def conduct_meeting(self):
        return f"{self.name} is conducting a meeting"

class Developer(Employee):
    def __init__(self, name, salary, language):
        super().__init__(name, salary)
        self.language = language
    
    def work(self):
        return f"{self.name} is coding in {self.language}"
    
    def debug(self):
        return f"{self.name} is debugging code"

print("=== INHERITANCE ===")
manager = Manager("Alice", 80000, 5)
dev = Developer("Bob", 60000, "Python")

print(f"Manager: {manager.work()}")        # Overridden
print(f"Developer: {dev.work()}")          # Overridden
print(f"Manager: {manager.get_pay()}")     # Inherited
print(f"Manager: {manager.conduct_meeting()}")  # New method


# ============================================================
# EXAMPLE 2: COMPOSITION APPROACH
# ============================================================

print("\n2. COMPOSITION APPROACH (HAS-A)")

class WorkBehavior:
    """Behavior component — can be composed into employees"""
    
    def __init__(self, role):
        self.role = role
    
    def do_work(self, name):
        return f"{name} is working as a {self.role}"

class Payment:
    """Payment component — can be composed into employees"""
    
    def __init__(self, salary):
        self.salary = salary
    
    def pay(self, name):
        return f"Paid {name} ${self.salary}"

class MeetingBehavior:
    """Meeting behavior — can be composed into employees"""
    
    def conduct_meeting(self, name):
        return f"{name} is conducting a meeting"

class CodingBehavior:
    """Coding behavior — can be composed into employees"""
    
    def code(self, name, language):
        return f"{name} is coding in {language}"

class EmployeeComposition:
    """Employee built from behaviors using composition"""
    
    def __init__(self, name, role, salary):
        self.name = name
        self.work_behavior = WorkBehavior(role)
        self.payment = Payment(salary)
    
    def work(self):
        return self.work_behavior.do_work(self.name)
    
    def get_pay(self):
        return self.payment.pay(self.name)

class ManagerComposition(EmployeeComposition):
    """Manager extends employee with additional behaviors"""
    
    def __init__(self, name, salary):
        super().__init__(name, "Manager", salary)
        self.meeting_behavior = MeetingBehavior()
    
    def conduct_meeting(self):
        return self.meeting_behavior.conduct_meeting(self.name)

class DeveloperComposition(EmployeeComposition):
    """Developer extends employee with additional behaviors"""
    
    def __init__(self, name, salary, language):
        super().__init__(name, "Developer", salary)
        self.coding_behavior = CodingBehavior()
        self.language = language
    
    def debug(self):
        return f"{self.name} is debugging code"
    
    def work(self):
        # Can override behavior
        return self.coding_behavior.code(self.name, self.language)

print("=== COMPOSITION ===")
manager2 = ManagerComposition("Alice", 80000)
dev2 = DeveloperComposition("Bob", 60000, "Python")

print(f"Manager: {manager2.work()}")        # Delegated
print(f"Developer: {dev2.work()}")          # Overridden/Delegated
print(f"Manager: {manager2.get_pay()}")     # Delegated
print(f"Manager: {manager2.conduct_meeting()}")  # Delegated


# ============================================================
# COMPARISON TABLE
# ============================================================

print("\n" + "=" * 60)
print("INHERITANCE vs COMPOSITION — COMPARISON TABLE")
print("=" * 60)
print("""
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                      │       INHERITANCE            │          COMPOSITION            │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ RELATIONSHIP         │ IS-A (a dog IS-A animal)     │ HAS-A (a car HAS-A engine)     │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ CODE REUSE           │ Inherits ALL parent methods  │ Uses components as needed      │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ FLEXIBILITY          │ Fixed at compile time        │ Can change at runtime          │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ COUPLING             │ Tightly coupled to parent    │ Loosely coupled to components  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ CHANGES              │ Parent changes affect child  │ Component changes isolated     │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ MULTIPLE REUSE       │ Single inheritance           │ Can use many components        │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ BEST FOR             │ Hierarchical relationships   │ Building complex objects       │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ TESTING              │ Harder (parent must work)    │ Easier (test components alone) │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ COMPLEXITY           │ Simpler to understand        │ More code, more structure      │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

 REMEMBER THE GOLDEN RULE:
   • Inheritance is for "IS-A" relationships
   • Composition is for "HAS-A" relationships
   • Prefer composition over inheritance
   • Inheritance is best for "is a special kind of"
""")

Comparison summary:

  • Relationship — inheritance = IS-A, composition = HAS-A
  • Coupling — inheritance creates tight coupling, composition creates loose coupling
  • Flexibility — composition is more flexible (can change components at runtime)
  • Code reuse — inheritance reuses everything, composition reuses only what you need
  • Testing — composition makes testing easier (components can be tested in isolation)

Quick Check: Which approach is more flexible: inheritance or composition? (Answer: Composition — you can swap components at runtime)

When to Use What

4

Making the Right Choice

Choosing between inheritance and composition is about understanding the relationship between your classes. Here's a simple way to think about it:

# When to Use Inheritance vs Composition

print("=" * 60)
print("WHEN TO USE INHERITANCE vs COMPOSITION")
print("=" * 60)

# ============================================================
# USE INHERITANCE WHEN:
# ============================================================

print("\n USE INHERITANCE WHEN:")

print("\n1. You have a genuine IS-A relationship")
class Vehicle:
    def move(self):
        return "Moving"

class Car(Vehicle):  # Car IS-A Vehicle
    pass

print("    Car IS-A Vehicle — good use of inheritance")

print("\n2. You want to share a common implementation")
class Logger:
    def log(self, message):
        print(f"LOG: {message}")

class FileLogger(Logger):
    def log(self, message):
        # Adds file writing but reuses the base behavior
        with open("log.txt", "a") as f:
            f.write(f"LOG: {message}\n")
        super().log(message)

print("    FileLogger adds file logging AND reuses base log")

print("\n3. You need to override behavior")
class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Woof!"

print("    Dog overrides speak() for its own behavior")


# ============================================================
# USE COMPOSITION WHEN:
# ============================================================

print("\n\n USE COMPOSITION WHEN:")

print("\n1. You have a HAS-A relationship")
class Engine:
    def start(self):
        return "Engine started"

class Car:
    def __init__(self):
        self.engine = Engine()  # Car HAS-A Engine

print("    Car HAS-A Engine — good use of composition")

print("\n2. You want to change behavior at runtime")
class PaymentMethod:
    def pay(self, amount):
        return f"Paid ${amount}"

class Cash(PaymentMethod):
    def pay(self, amount):
        return f"Paid ${amount} in cash"

class CreditCard(PaymentMethod):
    def pay(self, amount):
        return f"Paid ${amount} with credit card"

class Order:
    def __init__(self):
        self.payment_method = PaymentMethod()
    
    def set_payment_method(self, method):
        self.payment_method = method
    
    def pay(self, amount):
        return self.payment_method.pay(amount)

order = Order()
order.set_payment_method(Cash())
print(f"    Can switch payment method at runtime: {order.pay(100)}")

print("\n3. You want to use multiple behaviors from different sources")
class FlyBehavior:
    def fly(self):
        return "Flying"

class SwimBehavior:
    def swim(self):
        return "Swimming"

class Duck:
    def __init__(self):
        self.fly_behavior = FlyBehavior()
        self.swim_behavior = SwimBehavior()
    
    def fly(self):
        return self.fly_behavior.fly()
    
    def swim(self):
        return self.swim_behavior.swim()

print("    Duck uses fly AND swim behaviors via composition")


# ============================================================
# DECISION GUIDE
# ============================================================

print("\n" + "=" * 60)
print("DECISION GUIDE")
print("=" * 60)
print("""
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ ASK YOURSELF                        │ CHOOSE                                  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ "Is X a specialized kind of Y?"     │ INHERITANCE (Dog IS-A Animal)          │
│                                     │                                         │
│ "Does X have a Y?"                  │ COMPOSITION (Car HAS-A Engine)         │
│                                     │                                         │
│ "Will the relationship change?"     │ COMPOSITION (more flexible)            │
│                                     │                                         │
│ "Do I need to share code?"          │ Either (inheritance shares more)       │
│                                     │                                         │
│ "Is this a deep hierarchy?"         │ COMPOSITION (prevent deep inheritance) │
│                                     │                                         │
│ "Will this be used by others?"      │ COMPOSITION (more decoupled)           │
│                                     │                                         │
│ "Is this a simple, stable design?"  │ INHERITANCE (simpler to implement)     │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

 GOLDEN RULE: "Prefer composition over inheritance"
   Use inheritance only when it's the RIGHT tool for the job.
""")

Decision guide summary:

  • Inheritance — when you have a clear IS-A relationship and want to share code
  • Composition — when you have a HAS-A relationship and need flexibility
  • Prefer composition — it's more flexible and decoupled
  • Use inheritance wisely — only when it's the right tool
  • Both can coexist — many designs use both approaches

Quick Check: What's the golden rule for inheritance vs composition? (Answer: "Prefer composition over inheritance" — use inheritance only when it's the right tool)

Real-World Example

5

Building an E-commerce System

# Real-World Example: E-commerce System

from datetime import datetime
import uuid

print("=" * 60)
print("E-COMMERCE SYSTEM — INHERITANCE AND COMPOSITION")
print("=" * 60)

# ============================================================
# PART 1: INHERITANCE — User Types
# ============================================================

class User:
    """Base user class — uses inheritance for different user types"""
    
    def __init__(self, username, email):
        self.username = username
        self.email = email
        self.created_at = datetime.now()
    
    def get_info(self):
        return f"{self.username} ({self.email})"
    
    def get_role(self):
        return "user"

class Customer(User):
    """Customer IS-A User"""
    
    def __init__(self, username, email):
        super().__init__(username, email)
        self.cart = []
        self.order_history = []
    
    def get_role(self):
        return "customer"
    
    def add_to_cart(self, product):
        self.cart.append(product)
        return f"Added {product} to cart"
    
    def get_cart(self):
        return f"{len(self.cart)} items in cart"

class Admin(User):
    """Admin IS-A User"""
    
    def __init__(self, username, email):
        super().__init__(username, email)
        self.permissions = ["manage_users", "manage_products"]
    
    def get_role(self):
        return "admin"
    
    def manage_users(self):
        return "Managing users..."

class Vendor(User):
    """Vendor IS-A User"""
    
    def __init__(self, username, email):
        super().__init__(username, email)
        self.products = []
        self.sales = []
    
    def get_role(self):
        return "vendor"
    
    def add_product(self, product):
        self.products.append(product)
        return f"Added product: {product}"


# ============================================================
# PART 2: COMPOSITION — Order System
# ============================================================

class Product:
    """Product component — used in orders and carts"""
    
    def __init__(self, name, price, stock):
        self.id = str(uuid.uuid4())[:8]
        self.name = name
        self.price = price
        self.stock = stock
    
    def __str__(self):
        return f"{self.name} (${self.price})"

class Address:
    """Address component — used in orders"""
    
    def __init__(self, street, city, state, zip_code):
        self.street = street
        self.city = city
        self.state = state
        self.zip_code = zip_code
    
    def __str__(self):
        return f"{self.street}, {self.city}, {self.state} {self.zip_code}"

class OrderItem:
    """Order item component — used in orders"""
    
    def __init__(self, product, quantity):
        self.product = product
        self.quantity = quantity
        self.subtotal = product.price * quantity
    
    def __str__(self):
        return f"{self.quantity}x {self.product.name} (${self.subtotal:.2f})"

class Payment:
    """Payment component — used in orders"""
    
    def __init__(self, method, amount):
        self.method = method
        self.amount = amount
        self.status = "pending"
        self.transaction_id = str(uuid.uuid4())[:8]
    
    def process(self):
        self.status = "completed"
        return f"Payment of ${self.amount:.2f} via {self.method} processed"
    
    def __str__(self):
        return f"{self.method}: ${self.amount:.2f} ({self.status})"

class Order:
    """Order built from components using composition"""
    
    def __init__(self, customer, address, payment_method):
        # Composition: Order HAS-A customer, address, items, payment
        self.customer = customer
        self.address = address
        self.order_date = datetime.now()
        self.items = []
        self.payment = Payment(payment_method, 0)
        self.status = "pending"
    
    def add_item(self, product, quantity=1):
        if product.stock >= quantity:
            item = OrderItem(product, quantity)
            self.items.append(item)
            product.stock -= quantity
            return f"Added {quantity}x {product.name}"
        return f"Not enough stock for {product.name}"
    
    def calculate_total(self):
        total = sum(item.subtotal for item in self.items)
        self.payment.amount = total
        return total
    
    def process_order(self):
        if not self.items:
            return "Order has no items"
        
        total = self.calculate_total()
        payment_result = self.payment.process()
        self.status = "processing"
        
        return f"Order processed! Total: ${total:.2f} — {payment_result}"
    
    def get_summary(self):
        return {
            "order_id": str(uuid.uuid4())[:8],
            "customer": self.customer.get_info(),
            "address": str(self.address),
            "items": [str(item) for item in self.items],
            "total": f"${self.calculate_total():.2f}",
            "payment": str(self.payment),
            "status": self.status,
            "date": self.order_date.strftime("%Y-%m-%d %H:%M")
        }


# ============================================================
# DEMONSTRATION
# ============================================================

print("\n1. CREATING USERS (INHERITANCE)")
customer = Customer("alice", "alice@example.com")
admin = Admin("bob", "bob@example.com")

print(f"  Customer: {customer.get_info()} (Role: {customer.get_role()})")
print(f"  Admin: {admin.get_info()} (Role: {admin.get_role()})")

print("\n2. CREATING PRODUCTS")
product1 = Product("Laptop", 999.99, 5)
product2 = Product("Phone", 699.99, 10)
product3 = Product("Headphones", 149.99, 20)

print(f"  {product1}")
print(f"  {product2}")
print(f"  {product3}")

print("\n3. PLACING AN ORDER (COMPOSITION)")
address = Address("123 Main St", "Boston", "MA", "02101")
order = Order(customer, address, "credit_card")

print(f"  Order for: {customer.username}")
print(f"  Address: {address}")
print(f"  Adding items...")
print(f"  {order.add_item(product1, 1)}")
print(f"  {order.add_item(product2, 2)}")
print(f"  {order.add_item(product3, 3)}")

print(f"\n4. ORDER SUMMARY")
summary = order.get_summary()
print(f"  Order ID: {summary['order_id']}")
print(f"  Customer: {summary['customer']}")
print(f"  Total: {summary['total']}")
print(f"  Status: {summary['status']}")

print(f"\n5. PROCESSING ORDER")
print(f"  {order.process_order()}")

print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print(" INHERITANCE: Customer, Admin, Vendor all inherit from User")
print(" COMPOSITION: Order has Address, Payment, and OrderItem components")
print(" Both approaches work together in the same system")
print(" Choose inheritance for IS-A relationships (User types)")
print(" Choose composition for HAS-A relationships (Order components)")

Real-world example key points:

  • Inheritance used for user types — Customer, Admin, Vendor IS-A User
  • Composition used for order components — Order HAS-A Address, Payment, OrderItem
  • Both approaches work together — a complete system uses both
  • Clear separation of concerns — each component has a single responsibility

Quick Check: In the e-commerce example, what uses inheritance and what uses composition? (Answer: User types use inheritance; Order components use composition)

Best Practices

6

Using Inheritance and Composition Effectively

# Best Practices for Inheritance and Composition

print("=" * 60)
print("BEST PRACTICES")
print("=" * 60)

# ============================================================
# 1. USE INHERITANCE FOR GENUINE IS-A RELATIONSHIPS
# ============================================================

print("\n1. USE INHERITANCE FOR IS-A")

#  DO: Use inheritance when there's a clear IS-A relationship
class Animal:
    def breathe(self):
        return "Breathing"

class Dog(Animal):  # Dog IS-A Animal — good!
    def bark(self):
        return "Woof!"

#  DON'T: Use inheritance for HAS-A relationships
class Car(Engine):  # Car IS-A Engine? No! Car HAS-A Engine
    pass

# Instead, use composition:
class ProperCar:
    def __init__(self):
        self.engine = Engine()  # Car HAS-A Engine — good!


# ============================================================
# 2. USE COMPOSITION FOR FLEXIBILITY
# ============================================================

print("\n2. USE COMPOSITION FOR FLEXIBILITY")

class Renderer:
    def render(self, data):
        return f"Rendering: {data}"

class JSONRenderer(Renderer):
    def render(self, data):
        return f"JSON: {data}"

class XMLRenderer(Renderer):
    def render(self, data):
        return f"XML: {data}"

class DataProcessor:
    def __init__(self, renderer):
        self.renderer = renderer  # Composition: processor HAS-A renderer
    
    def process(self, data):
        return self.renderer.render(data)

# Can change renderer at runtime
processor = DataProcessor(JSONRenderer())
print(f"  JSON: {processor.process('data')}")

processor.renderer = XMLRenderer()
print(f"  XML: {processor.process('data')}")


# ============================================================
# 3. AVOID DEEP INHERITANCE HIERARCHIES
# ============================================================

print("\n3. AVOID DEEP HIERARCHIES")

#  DON'T: Create deep inheritance hierarchies
class A:
    pass

class B(A):
    pass

class C(B):
    pass

class D(C):
    pass

class E(D):
    pass
# This is hard to maintain and understand

#  DO: Use composition or keep hierarchies shallow
class Core:
    pass

class Feature1:
    pass

class Feature2:
    pass

class MyClass:
    def __init__(self):
        self.core = Core()
        self.feature1 = Feature1()
        self.feature2 = Feature2()


# ============================================================
# 4. USE ABCs FOR INTERFACES
# ============================================================

print("\n4. USE ABCs FOR INTERFACES")

from abc import ABC, abstractmethod

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass

class Circle(Drawable):
    def draw(self):
        return "Drawing circle"

class Square(Drawable):
    def draw(self):
        return "Drawing square"


# ============================================================
# 5. DELEGATE RESPONSIBILITY
# ============================================================

print("\n5. DELEGATE RESPONSIBILITY")

class Logger:
    def log(self, message):
        return f"LOG: {message}"

class EmailSender:
    def send(self, message):
        return f"EMAIL: {message}"

class Service:
    def __init__(self):
        self.logger = Logger()
        self.emailer = EmailSender()
    
    def process(self, data):
        # Delegate responsibilities
        self.logger.log(f"Processing {data}")
        self.emailer.send(f"Processed {data}")
        return "Processed"


# ============================================================
# 6. SUMMARY
# ============================================================

print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ PRACTICE            │ WHY IT MATTERS                                    │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ Use IS-A for        │ Ensures inheritance is used appropriately         │
│ inheritance         │                                                    │
│                     │                                                    │
│ Use HAS-A for       │ More flexible, easier to change                   │
│ composition         │                                                    │
│                     │                                                    │
│ Keep hierarchies    │ Easier to understand and maintain                 │
│ shallow             │                                                    │
│                     │                                                    │
│ Use ABCs for        │ Enforce contracts, better documentation           │
│ interfaces          │                                                    │
│                     │                                                    │
│ Delegate            │ Keeps classes focused and maintainable            │
│ responsibility      │                                                    │
│                     │                                                    │
│ Prefer composition  │ More flexible, less coupling, easier testing      │
│ over inheritance    │                                                    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

 REMEMBER:
   • Inheritance: "IS-A" (Dog IS-A Animal)
   • Composition: "HAS-A" (Car HAS-A Engine)
   • "Favor composition over inheritance" is a design principle
   • Both have their place — use the right tool for the job
""")

Best practices summary:

  • Use inheritance for IS-A — only when it's a genuine relationship
  • Use composition for HAS-A — more flexible and decoupled
  • Avoid deep hierarchies — keep inheritance shallow
  • Use ABCs for interfaces — enforce contracts where needed
  • Delegate responsibility — keep classes focused
  • Prefer composition — it's more flexible and easier to test

Quick Check: What's the main reason to prefer composition over inheritance? (Answer: Composition is more flexible, less coupled, and easier to test)

Try It Yourself

Experiment with inheritance and composition in the editor below.

Loading Pyodide... 0%
Python Code Editor
==================================================
INHERITANCE vs COMPOSITION - PRACTICE
==================================================

1. INHERITANCE (IS-A)
Laptop: Lenovo ThinkPad, RAM: 16GB
Phone: Samsung Galaxy, Camera: 50MP
ThinkPad is ON
Taking photo with 50MP camera

2. COMPOSITION (HAS-A)
Smartphone: Apple iPhone 15
Apple iPhone 15: Processing Instagram on 8 cores at 2.5GHz
Displaying 'Video' on 6.1 inch screen — Battery at 80%
Battery fully charged

3. COMPARISON

You've Got It!

You now understand the difference between inheritance and composition in Python. You know when to use each approach and how to design flexible, maintainable code.

Quick Quiz

Test what you've learned:

1. What type of relationship does inheritance represent?
2. What type of relationship does composition represent?
3. Which approach is generally more flexible?
4. What is the golden rule for inheritance vs composition?
5. Which statement about inheritance is TRUE?

Frequently Asked Questions

What's the difference between inheritance and composition? ā–¼

Inheritance is an IS-A relationship (a dog IS-A animal). Composition is a HAS-A relationship (a car HAS-A engine). Inheritance shares code through parent classes; composition builds objects from other objects.

Is composition better than inheritance? ā–¼

The general principle is "prefer composition over inheritance" because composition is more flexible and creates looser coupling. However, inheritance is still useful for genuine IS-A relationships. Use the right tool for the job.

Can I use inheritance and composition together? ā–¼

Yes! Many designs use both approaches. For example, you might use inheritance for the main type hierarchy (User → Customer, Admin, Vendor) and composition for building complex objects (Order uses Address, Payment, OrderItem components).

When should I use inheritance over composition? ā–¼

Use inheritance when you have a genuine IS-A relationship and want to share code. Use it when the relationship is stable and won't change. Use composition when you need flexibility, decoupling, or when you want to build objects from independent parts.

What is the "fragile base class" problem? ā–¼

The fragile base class problem occurs when changes to a parent class break child classes that depend on it. This is a risk with inheritance because children are tightly coupled to their parents. Composition reduces this risk because components are loosely coupled.

How do I choose between inheritance and composition? ā–¼

Ask yourself: "Is X a specialized kind of Y?" If yes, consider inheritance. "Does X have a Y?" If yes, use composition. Also consider flexibility, testing, and maintenance. When in doubt, prefer composition.

Where to Go From Here

Now that you understand the difference between inheritance and composition, check out these related topics:

Encapsulation

Learn how encapsulation works with inheritance and composition.

Learn More →

Polymorphism

Learn how polymorphism works with both inheritance and composition.

Learn More →

Inheritance in Python

Deep dive into inheritance and its various types.

Learn More →
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Overloading vs Overriding Next: Encapsulation →