whenever life put's you in a tough situtation, never say why me! but, try me!

OOP: Inheritance and Polymorphism

Inheritance and polymorphism are two of the most powerful features of Object-Oriented Programming (OOP). In this module, we will cover these concepts in detail, including how inheritance allows classes to inherit behavior from other classes, how polymorphism lets us treat different objects in a uniform way, and how method overriding works in Python.


Subtopic 1: Understanding Inheritance

Inheritance is a mechanism in OOP that allows a class (known as the child or subclass) to inherit the properties and behaviors (methods) of another class (the parent or base class). This promotes code reusability, where the child class can reuse, modify, or extend the functionality of the parent class.

Example of Inheritance:
# Parent class
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return f"{self.name} makes a sound."

# Child class inheriting from Animal
class Dog(Animal):
    def speak(self):
        return f"{self.name} barks."

# Child class inheriting from Animal
class Cat(Animal):
    def speak(self):
        return f"{self.name} meows."

# Creating objects
dog = Dog("Buddy")
cat = Cat("Whiskers")

print(dog.speak())  # Output: Buddy barks.
print(cat.speak())  # Output: Whiskers meows.

In this example:

  • Dog and Cat are subclasses of the Animal class.
  • Both subclasses inherit the __init__ method from Animal but override the speak method.

Subtopic 2: Method Overriding

Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its parent class. The method in the subclass replaces the method in the parent class when called on an instance of the subclass.

Example of Method Overriding:
class Parent:
    def show(self):
        return "This is the parent class."

class Child(Parent):
    def show(self):
        return "This is the child class."

# Creating objects
parent = Parent()
child = Child()

print(parent.show())  # Output: This is the parent class.
print(child.show())   # Output: This is the child class.

In this example:

  • The Child class overrides the show method from the Parent class to provide its own behavior.

Subtopic 3: Polymorphism in Python

Polymorphism allows methods in different classes to have the same name, but behave differently based on the type of object calling the method. It enables you to use a unified interface for different data types or objects.

Example of Polymorphism:
class Bird:
    def speak(self):
        return "Tweet tweet!"

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

# Creating objects
bird = Bird()
dog = Dog()

# Polymorphism: same method name, different behavior
print(bird.speak())  # Output: Tweet tweet!
print(dog.speak())   # Output: Woof!

In this example:

  • Both Bird and Dog have a method named speak(), but each class implements it differently. This is an example of polymorphism.

Polymorphism allows different objects (from different classes) to respond to the same method name with behavior specific to their class. It is commonly used in situations where you want to generalize operations across multiple object types.


Subtopic 4: Using super() Function

The super() function allows you to call methods from a parent class in a child class, especially useful when you override a method in a child class but still want to execute the original method in the parent class. It is commonly used in the constructor (__init__) to ensure the parent class is properly initialized.

Example of super() Function:
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound."

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Calls the __init__ method of Animal class
        self.breed = breed

    def speak(self):
        return f"{self.name} barks."

# Creating an object
dog = Dog("Buddy", "Golden Retriever")
print(dog.speak())  # Output: Buddy barks.

In this example:

  • The super().__init__(name) call in the Dog class constructor ensures that the name attribute is initialized using the __init__ method from the Animal class.
  • This allows the Dog class to extend the functionality of the Animal class while still leveraging its constructor.

Tasks

  1. Task 1: Animal Class and Inheritance

    • Create a base class Animal with methods eat() and sleep(). Create two subclasses Bird and Fish, each implementing their own version of the eat() method. Use inheritance to demonstrate shared behavior and method overriding.
  2. Task 2: Vehicle Class with Method Overriding

    • Create a Vehicle class with an info() method that returns "This is a vehicle". Then create two subclasses: Car and Bike. Override the info() method in both subclasses to include specific information for each vehicle type.
  3. Task 3: Employee Inheritance

    • Create a class Employee with attributes name and salary. Then create two subclasses Manager and Developer that inherit from Employee. Each subclass should have a method to print the employee's name, salary, and role.
  4. Task 4: Shape Class with Polymorphism

    • Define a Shape class with a method area(). Then create two subclasses, Rectangle and Circle, each implementing its own version of area(). Create instances of both classes and call area() to demonstrate polymorphism.
  5. Task 5: Bank Account with super()

    • Create a class BankAccount with a method deposit() and a balance attribute. Create a subclass SavingsAccount that inherits from BankAccount and adds a withdraw() method. Use super() to call the deposit() method from the parent class.
  6. Task 6: Method Overriding and Super

    • Create a Person class with a method greet(). Create a subclass Employee that overrides the greet() method. Use super() to call the greet() method from the Person class and extend it with an additional message.