Home » Python Method Overriding Tutorial

Python Method Overriding Tutorial

Method overriding is a concept in object-oriented programming where a subclass provides a specific implementation of a method that is already defined in its superclass.

Method overriding allows a subclass to modify or completely replace the behavior of a method inherited from its parent class. This enables polymorphism, which is a key feature of object-oriented programming.

In Python, method overriding occurs dynamically at runtime, which means Python decides which method to call (the one from the parent class or the overridden one in the subclass) based on the type of the object.

In this tutorial, we will explore method overriding with practical examples, and how it can be used to customize behavior in subclasses while preserving the functionality of the superclass.

1. Basic Method Overriding

When a method in a subclass has the same name and signature as a method in its superclass, the method in the subclass overrides the method in the superclass.

Example: Basic Method Overriding

# Example: Basic method overriding
class Animal:
    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow"

# Creating objects of Dog and Cat
dog = Dog()
cat = Cat()

# Calling the overridden methods
print(dog.sound())  # Output: Bark
print(cat.sound())  # Output: Meow

In this example:

The Dog and Cat classes override the sound() method of the Animal class to provide their specific implementations (Bark for Dog and Meow for Cat).
The object of the subclass (dog and cat) calls the overridden method, not the one from the parent class.

2. Accessing the Parent Class Method Using super()

Sometimes, you want to extend the behavior of the parent class’s method instead of completely replacing it. You can use the super() function to call the method from the parent class and add additional behavior.

Example: Using super() to Extend Parent Class Method

# Example: Using super() to access the parent class method
class Animal:
    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def sound(self):
        # Call the parent class method using super()
        parent_sound = super().sound()
        return f"{parent_sound} and also Bark"

# Creating an object of Dog
dog = Dog()

# Calling the overridden method
print(dog.sound())  # Output: Some generic animal sound and also Bark

In this example:

The Dog class overrides the sound() method but also calls the parent class method (Animal.sound()) using super() to keep the base functionality.
This allows you to add to the behavior rather than completely replacing it.

3. Method Overriding with Constructor (__init__())

You can also override the constructor (__init__()) of the parent class in the subclass. If you override __init__(), the subclass’s constructor will be executed instead of the parent class’s constructor. You can still call the parent class’s constructor using super().

Example: Overriding the Constructor

# Example: Overriding the constructor (__init__())
class Animal:
    def __init__(self, name):
        self.name = name

    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def __init__(self, name, breed):
        # Call the parent class constructor using super()
        super().__init__(name)
        self.breed = breed

    def sound(self):
        return f"{self.name} says Bark"

# Creating an object of Dog
dog = Dog("Buddy", "Golden Retriever")

# Accessing attributes and calling the overridden method
print(dog.name)  # Output: Buddy
print(dog.breed)  # Output: Golden Retriever
print(dog.sound())  # Output: Buddy says Bark

In this example:

The Dog class overrides the parent class constructor (__init__()), adds a new attribute breed, and still uses super() to call the Animal class constructor to set the name attribute.

4. Method Overriding and Polymorphism

Method overriding allows for polymorphism, where a method in a subclass behaves differently based on the object type at runtime. This enables a subclass to provide its specific implementation of a method while still adhering to a common interface defined by the superclass.

Example: Polymorphism with Method Overriding

# Example: Polymorphism with method overriding
class Animal:
    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow"

def make_animal_sound(animal):
    print(animal.sound())

# Creating different objects
dog = Dog()
cat = Cat()

# Using polymorphism - the same function call behaves differently
make_animal_sound(dog)  # Output: Bark
make_animal_sound(cat)  # Output: Meow

In this example:

The make_animal_sound() function can accept any object that is a subclass of Animal, and it will dynamically call the overridden sound() method based on the object type (Dog or Cat).

5. Overriding Private Methods

Private methods in Python are typically prefixed with double underscores (e.g., __method). Although Python does not truly support private methods (they can still be accessed in certain ways), the name-mangling feature makes it harder to override private methods. However, you can still override methods that are meant to be “private” by knowing the mangled name.

Example: Overriding Private Methods

# Example: Overriding a private method
class Animal:
    def __sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    # Overriding the private method
    def _Animal__sound(self):
        return "Bark"

dog = Dog()
# Accessing the "private" method using name mangling
print(dog._Animal__sound())  # Output: Bark

In this example:

The Dog class overrides the private method __sound() of the Animal class using name mangling (_Animal__sound), which is how Python internally renames private methods.

6. Method Resolution Order (MRO)

Python follows the Method Resolution Order (MRO) to determine which method to call when there are multiple inheritance paths. The MRO dictates the order in which classes are checked for a method or attribute.

You can check the MRO of a class using the mro() method or the __mro__ attribute.

Example: MRO in Multiple Inheritance

# Example: MRO in multiple inheritance
class Animal:
    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Robot:
    def sound(self):
        return "Beep"

class RoboDog(Dog, Robot):
    pass

# Creating an object of RoboDog
robodog = RoboDog()

# Method Resolution Order (MRO) decides which method to call
print(robodog.sound())  # Output: Bark

# Checking the MRO
print(RoboDog.mro())  # Output: [<class '__main__.RoboDog'>, <class '__main__.Dog'>, <class '__main__.Animal'>, <class '__main__.Robot'>, <class 'object'>]

In this example:

RoboDog inherits from both Dog and Robot. Python’s MRO ensures that the Dog class’s sound() method is called first.
The MRO can be checked with the mro() method, showing the order in which Python searches for methods.

7. Overriding Class Methods

Class methods can also be overridden in the subclass. These methods, which are defined using the @classmethod decorator, are called on the class itself rather than on an instance.

Example: Overriding Class Methods

# Example: Overriding class methods
class Animal:
    @classmethod
    def description(cls):
        return "This is an animal"

class Dog(Animal):
    @classmethod
    def description(cls):
        return "This is a dog"

# Calling the class method on the class itself
print(Animal.description())  # Output: This is an animal
print(Dog.description())     # Output: This is a dog

In this example:

The description() class method is overridden in the Dog class, and the correct version is called depending on whether the method is called on Animal or Dog.

8. Best Practices for Method Overriding

Use super(): Always use super() to call the parent class’s method when extending functionality, as it ensures that all classes in the inheritance chain are properly initialized.
Avoid overriding private methods: While technically possible, overriding private methods (name-mangled methods) should generally be avoided to keep the design clean and follow encapsulation principles.
Follow the Liskov Substitution Principle: When overriding methods, ensure that the subclass can be used in place of the superclass without breaking the behavior expected from the superclass. This maintains polymorphism correctly.

Summary

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass, enabling polymorphism and customization.
The super() function is used to call methods from the parent class, allowing you to extend the functionality of the inherited method instead of completely replacing it.
Polymorphism allows methods to be overridden in different subclasses while maintaining a common interface, enabling flexible and reusable code.
Python’s Method Resolution Order (MRO) determines the order in which methods are resolved when multiple inheritance is involved.
Overriding works for regular instance methods, constructors (__init__()), and class methods defined using @classmethod.

By understanding and applying method overriding, you can create more flexible, reusable, and maintainable object-oriented programs in Python.

You may also like

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More