# Polymorphism

Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common superclass.

### Method Overriding

* Method overriding occurs when a subclass defines a method with the same name as a method in its superclass.
    
* The method in the subclass overrides the implementation of the method in the superclass.
    
* This allows for different implementations of the same method across different classes.
    

Here's an example of method overriding in Python:

```python
class Animal:
    def sound(self):
        print("Animal makes a sound")

class Dog(Animal):
    def sound(self):
        print("Dog barks")


# Creating instances of different classes
animal = Animal()
dog = Dog()

# Calling the overridden method
animal.sound()  # Output: Animal makes a sound
dog.sound()     # Output: Dog barks
```

In the example above, the `Animal` class has a method `make_sound()`. The `Dog` class inherit from the `Animal` class and override the `make_sound()` method with their specific implementations. When `make_sound()` is called on an instance of each class, the overridden method in the respective subclass is invoked.

### Method Overloading

* Method overloading refers to defining multiple methods with the same name but different parameters within a class.
    
* Python doesn't support method overloading in the same way as some other languages (e.g., Java), where methods can have different signatures.
    
* However, you can achieve a form of method overloading using default argument values or variable-length arguments.
    

Here's an example demonstrating method overloading using default argument values:

```python
class Calculator:
    def add(self, a, b):
        return a + b

    def add(self, a, b, c):
        return a + b + c

# Creating an instance of the Calculator class
calculator = Calculator()

# Calling the overloaded methods
result1 = calculator.add(2, 3)
result2 = calculator.add(2, 3, 4)

print(result1)  # Output: 5
print(result2)  # Output: 9
```

In this example, the `Calculator` class has two `add()` methods with different parameter counts. By providing different parameter lists, we can achieve a form of method overloading.
