Inheritance and Composition in Python

In object-oriented programming (OOP), Inheritance and Composition are two fundamental concepts used to establish relationships between classes. These concepts help in reusing code and building complex systems efficiently.


What is Inheritance?

Inheritance allows a class (called a child class or subclass) to inherit attributes and methods from another class (called a parent class or superclass). This enables code reusability and establishes an “is-a” relationship between classes.

Syntax

</>
Copy
class ParentClass:
    # Parent class methods and attributes

class ChildClass(ParentClass):
    # Child class can access methods and attributes of ParentClass

Examples of Inheritance

1. Basic Inheritance Example

In this example, we define a Parent class and a Child class that inherits from it.

When the child class inherits from the parent class, it automatically gets access to the parent’s methods and attributes without redefining them.

</>
Copy
class Animal:
    def speak(self):
        print("Animal speaks")

class Dog(Animal):  # Dog class inherits from Animal class
    def bark(self):
        print("Dog barks")

# Creating an object of the Dog class
dog = Dog()
dog.speak()  # Inherited method
dog.bark()   # Method of Dog class

Output:

Animal speaks
Dog barks

Here, the Dog class inherits the speak() method from the Animal class and also defines its own method, bark().

2. Method Overriding in Inheritance

Sometimes, a child class may need to provide its own version of a method inherited from the parent class. This is called method overriding.

</>
Copy
class Animal:
    def speak(self):
        print("Animal makes a sound")

class Cat(Animal):
    def speak(self):  # Overriding the parent class method
        print("Cat meows")

# Creating an object of Cat
cat = Cat()
cat.speak()  # Calls the overridden method in the Cat class

Output:

Cat meows

Here, the Cat class overrides the speak() method from the Animal class to provide its own behavior.


What is Composition?

Composition is another way to reuse code in OOP. Instead of inheriting from another class, one class contains an instance of another class as an attribute. This establishes a “has-a” relationship rather than an “is-a” relationship.

Syntax

</>
Copy
class ClassA:
    # Methods and attributes

class ClassB:
    def __init__(self):
        self.object_a = ClassA()  # Creating an instance of ClassA inside ClassB

Examples of Composition

1. Basic Composition Example

Here, we define a Engine class and a Car class that contains an instance of Engine.

</>
Copy
class Engine:
    def start(self):
        print("Engine starts")

class Car:
    def __init__(self):
        self.engine = Engine()  # Composition: Car has an Engine

    def drive(self):
        print("Car is driving")
        self.engine.start()  # Using the Engine's start method

# Creating an object of Car
car = Car()
car.drive()

Output:

Car is driving
Engine starts

Here, the Car class does not inherit from Engine. Instead, it contains an Engine instance, meaning Car “has an” Engine. This allows us to reuse the functionality of the Engine class inside the Car class.


Difference Between Inheritance and Composition

FeatureInheritanceComposition
Relationship Type“is-a”“has-a”
Code ReuseCode is reused by extending parent classCode is reused by including instances of other classes
FlexibilityLess flexible; changing the parent class affects all child classesMore flexible; changes in one class do not affect others
ComplexityCan become complex due to deep inheritance hierarchiesMore modular and maintainable

Both inheritance and composition are useful in different scenarios. Inheritance is ideal when a subclass truly extends the functionality of a parent class, whereas composition is preferred when different classes work together without tightly coupling them.