Published: 20 May 2025 | Reading Time: 9 minutes
Method overloading and method overriding are fundamental concepts in object-oriented programming that improve code adaptability. Although method overloading allows the definition of multiple methods with the same name that vary in their parameters, Python doesn't offer direct support for this because of its dynamic typing characteristics. Instead, Python uses techniques like default arguments and *args/**kwargs to mimic overloading. Method overriding, however, is fully supported, allowing subclasses to customize methods inherited from parent classes. This guide explains how to use both method overloading and method overriding in Python, with practical examples for better understanding.
Method overloading refers to an object-oriented programming feature that allows different methods in the same class to share the same name, with variations in their parameters. These differences can include the number of parameters, their data types, or the order in which they appear. It enhances code readability and flexibility by enabling developers to use a single method name for similar actions. The selection of the method to be executed is done at compile time, which depends on the arguments passed.
Method Overloading in Python entails the capability to create several methods that share the same name while having different parameters within a single class. This allows a method to perform different tasks based on the arguments passed to it. Despite Python's lack of support for the classical method overloading found in languages like Java and C++, default parameters, variable-length arguments (*args and **kwargs), or additional libraries like multipledispatch can accomplish comparable results. These techniques enable a single method to handle various types and numbers of inputs, enhancing code flexibility and readability.
Custom Constructors in Classes: Method overloading is widely used to create multiple constructors in a class, allowing objects to be initialized with different sets of data based on the use case.
Mathematical Operations: Overloaded methods can handle calculations for different data types (like int, float, or double) while using the same method name, making arithmetic operations more flexible.
Utility Functions (e.g., Printing):
Functions like print() or log() can be overloaded to accept different data types or formats, simplifying how messages or data are displayed or logged.
Even though Python does not have built-in method overloading, you can achieve similar behavior using these techniques:
*args, **kwargs): Python functions can take a dynamic number of arguments by using *args for positional arguments and **kwargs for keyword arguments.multipledispatch library allows function overloading based on the argument types.+ and - work with your objects by defining special methods in your class.__init__ method.By assigning default values to parameters, we can make a single function work in multiple ways. If no value is supplied, the default value for a parameter is used.
class Greeting:
def greet(self, name, age=None): # age has a default value of None
if age is not None:
print(f"Hello, {name}! You are {age} years old.")
else:
print(f"Hello, {name}!")
# Creating an instance of the class
obj = Greeting()
# Calling the method with one argument
obj.greet("Alice") # Output: Hello, Alice!
# Calling the method with two arguments
obj.greet("Bob", 25) # Output: Hello, Bob! You are 25 years old.
Hello, Alice!
Hello, Bob! You are 25 years old.
In this example, the greet method has a second parameter, age, with a default value of None. The method prints the person's name and age in a message if the caller enters a value for age. If a value is absent, it only shows the name. This approach allows the same function to work in different ways.
*args, **kwargs)Using **kwargs for keyword arguments and *args for positional arguments, you can supply a variable number of parameters to a method.
class Calculator:
def add(self, *args):
total = sum(args)
print(f"Sum: {total}")
# Creating an instance of the class
calc = Calculator()
# Calling the method with different number of arguments
calc.add(5, 10) # Output: Sum: 15
calc.add(1, 2, 3, 4, 5) # Output: Sum: 15
Sum: 15
Sum: 15
The add method accepts any number of arguments using *args and calculates their sum. This allows the same method to behave differently based on the number of inputs.
This external library allows method overloading based on the types of arguments.
from multipledispatch import dispatch
class Display:
@dispatch(int)
def show(self, a):
print(f"Integer: {a}")
@dispatch(str)
def show(self, a):
print(f"String: {a}")
# Creating an instance of the class
obj = Display()
obj.show(10) # Output: Integer: 10
obj.show("Hi") # Output: String: Hi
Integer: 10
String: Hi
The dispatch decorator allows you to define multiple versions of a method based on argument types, simulating method overloading.
Decorators can be used to select which function to call at runtime depending on the input.
def overload(func):
registry = {}
def register(arg_type):
def inner(f):
registry[arg_type] = f
return f
return inner
def wrapper(arg):
return registry[type(arg)](arg)
wrapper.register = register
return wrapper
@overload
def process(x):
pass
@process.register(int)
def _(x):
print(f"Processing integer: {x}")
@process.register(str)
def _(x):
print(f"Processing string: {x}")
# Calling the function
process(10) # Output: Processing integer: 10
process("Hi") # Output: Processing string: Hi
Processing integer: 10
Processing string: Hi
This custom decorator stores multiple functions and selects the appropriate one based on the argument type, mimicking function overloading.
You can redefine how operators behave for user-defined objects by overriding special methods like __add__.
class Point:
def __init__(self, x, y):
self.e = e
self.f = f
def __add__(self, other):
return Point(self.e + other.e, self.f + other.f)
def display(self):
print(f"Point({self.e}, {self.f})")
# Creating objects
p1 = Point(1, 2)
p2 = Point(3, 4)
result = p1 + p2
result.display() # Output: Point(4, 6)
Point(4, 6)
By defining the __add__ method, we allow the + operator to work with Point objects, combining their coordinates.
Although Python doesn't support multiple constructors, you can simulate it using default arguments or conditional logic inside the __init__ method.
class Book:
def __init__(self, title="Unknown", author="Unknown"):
self.title = title
self.author = author
def show(self):
print(f"Book: {self.title} by {self.author}")
# Creating instances with different arguments
book1 = Book()
book2 = Book("Python Basics")
book3 = Book("Data Science", "John Doe")
book1.show() # Output: Book: Unknown by Unknown
book2.show() # Output: Book: Python Basics by Unknown
book3.show() # Output: Book: Data Science by John Doe
Book: Unknown by Unknown
Book: Python Basics by Unknown
Book: Data Science by John Doe
The constructor accepts optional parameters. Depending on how many arguments are passed, it behaves accordingly, mimicking multiple constructors.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Using Default Arguments | O(1) | O(1) |
| Using Variable-Length Arguments | O(n) – depends on the number of args | O(n) – stores all arguments |
| Using the multipledispatch Library | O(1) for dispatching O(n) for multiple registrations | O(n) – registry storage |
| Using Function Decorators | O(1) for dispatch O(n) if checking type manually | O(n) – stores functions in registry |
| Using Operator Overloading | O(1) | O(1) |
| Using Constructor Overloading | O(1) | O(1) |
When a subclass defines a method with the same name and parameters as one in its superclass, the subclass's method takes precedence when called. This supports dynamic polymorphism, meaning different classes can implement methods in their unique way while maintaining a common interface.
Method overriding in Python allows a subclass to provide a specific implementation of a method that is already defined in its parent class. This helps customize or extend the behavior of inherited methods without changing the original code. It promotes code reuse and makes programs more flexible and easier to maintain.
Python supports method overriding out of the box through inheritance. You can define a method in a subclass with the same name as in the parent class to change or extend its behavior.
Key techniques and best practices:
Define a method in the child class with the same name and parameters as in the parent class. This overrides the parent's implementation.
Use the super() function inside the child class to access and reuse the parent's method, allowing you to extend rather than replace behavior.
You can treat objects of the child class as the parent class, and Python will still call the child's version of the overridden method at runtime.
In multi-level inheritance, methods can be overridden step-by-step at each level to customize behavior for each subclass.
You can force method overriding by marking methods in the parent class as abstract using Python's abc module.
super().To implement method overriding, start by creating a parent class that contains a method. Then, create a subclass that inherits from this parent class. Within the subclass, redefine the method with the same name and parameters as in the parent class.
This allows the subclass to provide its specific implementation of the method while maintaining the same method signature. Method Overloading and Method Overriding in Python are useful when a subclass needs to modify or extend the behaviour of a process without changing its name or structure, ensuring consistency while allowing customisation.
class Employee:
def work(self):
print("Employee is working...")
class Developer(Employee):
def work(self):
print("Developer is writing code.")
class Designer(Employee):
def work(self):
print("Designer is creating user interfaces.")
# Creating objects
dev = Developer()
des = Designer()
dev.work() # Output: Developer is writing code.
des.work() # Output: Designer is creating user interfaces.
Developer is writing code.
Designer is creating user interfaces.
This program shows method overriding in Python using inheritance. The Animal class has a method called make_sound, which prints a generic message. The Dog and Cat classes inherit from Animal but override the make_sound method to provide their specific implementations, Bark for dogs and Meow for cats. When we create instances of Dog and Cat and call make_sound, Python executes the overridden methods, displaying the respective outputs.
| Feature | Method Overloading | Method Overriding |
|---|---|---|
| Definition | Creating multiple methods in the same class that share the same name but accept different sets of parameters. | Redeclaring a method in a subclass that already has the parent class, so the former method will be away in the subclass, having the same name and parameters. |
| Class Requirement | This happens within a single class. | It requires a parent-child relationship (inheritance) between classes. |
| Method Signature | The methods need to be titled similarly but their parameter types, or the sequence, or the counts have to be different. | A method must have the exact same name, parameter list, and return type as the method in the superclass in order to successfully override one from the parent class. |
| Access Modifiers | Overloaded methods can have varying access modifiers. | An overriding method cannot use a stricter access modifier than the method it overrides. |
| Return Type | It can have different return types. | It must have the same return type (or a subclass type, known as covariant return). |
| Invocation | It is resolved at compile-time based on the method signature. | It is resolved at runtime using the dynamic method dispatch. |
| Purpose | It provides multiple ways to operate using the same method name but with different inputs. | It enables a subclass to modify or expand the functionality of a parent class method. |
| Performance Impact | It improves readability and code organization, but can slightly increase memory usage due to multiple method definitions. | It can affect performance if improperly used, as runtime decisions may slow execution. |
| Use Case | When multiple versions of a method are needed to handle different input types or numbers. | When a subclass has a requirement to change the functionality of an inherited method for its particular purpose. |
In Python, true method overloading like in Java or C++ isn't supported. However, you can simulate it using default arguments, *args, **kwargs, or use libraries like functools.
Use default parameter values or *args/**kwargs to handle multiple use cases in a single method.
Use functools.singledispatch to create type-based function overloading. It keeps code clean and organized by separating logic for different types.
Don't overload methods with too many argument combinations. It becomes harder to test and maintain. Keep conditions simple.
Document expected input types and behaviors for better readability, especially when using variable arguments.
If your overloaded method supports different argument types or counts, test each combination to ensure the logic works correctly.
Method overriding enables a subclass to change or enhance the functionality of a method inherited from its parent class. Python supports this natively through inheritance.
When extending behavior, always call the parent class method using super() to avoid duplicating logic and maintain consistency.
Keep the method name and parameters consistent with the base class to prevent confusion and maintain polymorphism.
A subclass should be usable in place of its superclass without altering the expected behavior of the program.
Override a method only when you genuinely need to change or extend its functionality.
Clearly explain what changes or additions the overridden method introduces compared to the base method.
*args, **kwargs)There is one more way to simulate method overloading in Python, and that is by employing variable-length arguments. These allow a function to accept a flexible number of parameters.
*args (non-keyword arguments) allow passing multiple values as a tuple.**kwargs (keyword arguments) allow passing multiple named parameters as a dictionary.def add(*args): # Accepts any number of arguments
return sum(args) # Returns back the total of all the arguments
print(add(2, 3))
print(add(1, 2, 3, 4, 5))
5
15
The add method can take an unlimited number of arguments because it utilizes *args in our case. The sum() function then calculates the total sum of all values passed to it. This eliminates the need to define multiple overloaded functions for different numbers of parameters.
sum() iterates through all elements).Python does not support method overloading by default, but the multipledispatch library allows us to achieve function overloading based on argument types. This means we can define multiple versions of a function, and Python will choose the appropriate one based on the types of arguments passed.
from multipledispatch import dispatch
@dispatch(int, int)
def add(a, b):
return a + b # Adds two integers
@dispatch(str, str)
def add(a, b):
return a + " " + b # Concatenates two strings with a space
# Testing the function
print(add(5, 10)) # Output: 15
print(add("Hello", "World")) # Output: Hello World
15
Hello World
The @dispatch decorator is used to create multiple versions of the add function. The first version accepts two integers and returns their sum, while the second version takes two strings and joins them with a space. When calling add(5, 10), Python selects the integer version, and when calling add("Hello", "World"), it selects the string version. This mimics function overloading by automatically choosing the correct function based on argument types.
Another way to simulate method overloading in Python is by using the @singledispatch decorator from the functools module. This allows us to create multiple versions of a function that behave differently depending on the type of argument passed.
from functools import singledispatch
@singledispatch
def greet(arg):
raise NotImplementedError("Unsupported type") # Default behavior for unsupported types
@greet.register(str)
def _(arg):
print(f"Hello, {arg}!") # Handles string input
@greet.register(int)
def _(arg):
print(f"Hello, number {arg}!") # Handles integer input
# Calling the function with different types
greet("Alice") # Output: Hello, Alice!
greet(42) # Output: Hello, number 42!
Hello, Alice!
Hello, number 42!
Here, @singledispatch makes greet a generic function. The default version raises an error if an unsupported type is passed. The @greet.register(str) and @greet.register(int) decorators create specific versions of greet for string and integer arguments. When the function is called, Python selects the correct version based on the argument's type.
In Python, we can redefine the behavior of built-in operators like +, -, and * by implementing special methods (also known as magic methods). This allows us to use operators with user-defined objects just like we do with primitive data types.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(2, 3)
p2 = Point(4, 5)
print(p1 + p2)
Point(6, 8)
In this example, we define a Point class that represents a point in a 2D plane with x and y coordinates. The __add__ method is implemented to overload the + operator, allowing two Point objects to be added together. When p1 + p2 is executed, Python internally calls p1.__add__(p2), adding the corresponding coordinates of p1 and p2, and returning a new Point object with the summed values. The __repr__ method ensures that when the object is printed, it appears in a readable format (Point(x, y)).
Point object is created, requiring a fixed amount of extra space.Python does not support multiple constructors (methods with the same name but different parameters). However, we can achieve similar functionality using default arguments or *args. This allows us to define a single constructor that can handle different numbers of arguments.
class Vehicle:
def __init__(self, make, model=None, year=None):
self.make = make
self.model = model if model else "Unknown"
self.year = year if year else "Unknown"
def display_info(self):
print(f"Make: {self.make}, Model: {self.model}, Year: {self.year}")
v1 = Vehicle("Toyota")
v2 = Vehicle("Honda", "Civic", 2020)
v1.display_info()
v2.display_info()
Make: Toyota, Model: Unknown, Year: Unknown
Make: Honda, Model: Civic, Year: 2020
In this example, we define a Vehicle class with a single constructor (__init__). Instead of defining multiple constructors, we use default arguments (model=None, year=None). If the user provides values for model and year, they are assigned normally. If not, the attributes default to "Unknown".
When we create v1 with only the make argument, the missing model and year fields are set to "Unknown". For v2, all three arguments are provided, so they are assigned directly. The display_info method prints the details of the vehicle.
The super() function lets a child class access methods from its parent class, enabling you to build upon or customize the original behavior without rewriting the entire method. This promotes code reuse and cleaner inheritance.
class Animal:
def make_sound(self):
print("Some generic animal sound")
class Dog(Animal):
def make_sound(self):
super().make_sound() # Calls parent class method
print("Bark")
dog = Dog()
dog.make_sound()
Some generic animal sound
Bark
This program shows method overriding with the use of super(). The Animal class has a method make_sound that prints a generic message. The Dog class inherits from Animal and overrides the make_sound method. Inside the overridden method, super().make_sound() is called, which executes the parent class's make_sound method before executing print("Bark"). As a result, when dog.make_sound() is called, it first prints "Some generic animal sound" from the parent class and then "Bark" from the Dog class.
When a class derives from more than one parent class, Python adheres to a particular sequence known as Method Resolution Order (MRO) to ascertain which method is executed when invoked. Both method overloading and method overriding in Python allow subclasses to modify or extend the behavior of methods. The super() function allows a subclass to call the parent class's version of a method even if it provides its own implementation of the method that is already defined in the parent class. This allows the subclass to retain the functionality of the original method while introducing additional features.
class A:
def display(self):
print("Display from A")
class B:
def display(self):
print("Display from B")
class C(A, B): # Inherits from both A and B
def display(self):
super().display() # Calls A's display method
print("Display from C")
c = C()
c.display()
Display from A
Display from C
In the earlier example, classes A and B are the ancestors of class C. Since C(A, B) is defined, Python follows the MRO and looks at A before B. Inside C, the display() method is overridden, but it first calls super().display(). In this case, super() refers to A, so A's display() method runs first, printing "Display from A". After that, C's print statement executes, displaying "Display from C".
A class can inherit from another class through multilevel inheritance when the other class inherits from the original class. This forms a chain of inheritance. A function in a derived class is method overriding if it maintains the method activation and is executed by a procedure that shares the base class's name. In Python, the principles of method overloading and method overriding are crucial for creating adaptable and reusable code structures.
# Base class
class Vehicle:
def drive(self):
print("Driving a vehicle")
# Intermediate class inheriting from Vehicle
class Car(Vehicle):
def drive(self):
print("Driving a car")
# Derived class inheriting from Car
class SportsCar(Car):
def drive(self):
super().drive() # Calls the drive() method from the Car class
print("Driving a sports car")
# Creating an instance of SportsCar
sports_car = SportsCar()
sports_car.drive()
Driving a car
Driving a sports car
sports_car.drive(), it first looks for the drive() method inside the SportsCar class.super().drive() statement calls the drive() method of the immediate parent class (Car).Car class's drive() method runs first, printing "Driving a car".SportsCar class continues executing its drive() method, printing "Driving a sports car".Method Overloading and Method Overriding in Python are important concepts in object-oriented programming that improve code efficiency and flexibility. In Python, traditional method overloading is not directly supported, but similar functionality can be achieved using default parameters, variable-length arguments, or external libraries like multiple dispatch. Meanwhile, method overriding is fully supported in Python, allowing subclasses to redefine methods from a parent class.
Method overloading enables defining multiple methods with the same name but distinct parameter lists. Python doesn't support it natively, but you can achieve similar functionality using default values or variable-length arguments (*args, **kwargs).
Method overriding happens when a subclass provides a new version of a method already defined in its parent class. The act of defining many methods with the same name but distinct parameters inside a single class is known as "method overloading."
No, Python does not support traditional method overloading like Java or C++. Instead, it provides flexible argument handling using default parameters and variable-length arguments to achieve similar results.
You can mimic method overloading using default arguments, *args, **kwargs, or by using the multipledispatch library, which allows defining multiple methods with the same name but different argument types.
The super() function enables a subclass to call methods from its parent class. This is useful when you want to extend or modify an inherited method without completely replacing it.
Python doesn't allow multiple methods with the same name but different parameter types. Instead, you can handle different types using *args, **kwargs, or by checking argument types within a single method.
If you define multiple methods with the same name in a class, only the last one will be used. Python does not keep multiple versions, so earlier definitions get overwritten.
Python uses the Method Resolution Order (MRO) to determine which method to call when there are multiple parent classes. The MRO follows a specific order to avoid ambiguity in method overriding.
Yes, constructors (__init__ methods) can also be overridden in Python subclasses to customize the initialization process while optionally calling the parent class's constructor using super().
Yes, decorators can be used to simulate method overloading by intercepting function calls and routing them based on argument types or counts.
In Python, method overriding refers specifically to replacing a method in a subclass, while function overriding is a broader term often used interchangeably, but usually refers to functions outside of classes.
Source: NxtWave - CCBP Blog
Contact: [email protected] | +919390111761 (WhatsApp only)