How to Increase Code Flexibility with Function and Method Overloading

In this post, code flexibility refers to how much existing code needs to be changed when new requirements arise. A flexible piece of code is one where new needs can be addressed simply by adding new code — without having to modify what’s already there. In software design, this is known as the Open/Closed Principle, which states that well-designed code should be open to extension but closed to modification.

Why is this important? Because once a piece of code has been thoroughly tested and proven to work, modifying it introduces a risk of bugs — and that means retesting, which can be expensive and time-consuming in a large codebase.

Example: Modeling a Recycling Plant

Let’s say we’re building a model of a recycling plant. Initially, the plant only processes metal waste. So, in our model, we define a MetalWaste class and a RecyclingPlant class with a process() method like this:

Later, the plant gains the ability to process plastic waste as well.

The question is: how should we reflect this new capability in our class design?

Option 1: Use Type Checks Inside the Method

One solution is to modify the existing process() method to accept both metal and plastic, then use if statements or type checks to handle each case accordingly. However, this approach violates the Open/Closed Principle. Each new waste type would require editing the method body, adding more conditional branches.

Option 2: Use Separate Methods for Each Type

Another idea is to define a separate method for each type of waste:

This keeps the code extensible, but it introduces another issue: the calling code must also change depending on the waste type. That becomes problematic if the calling code is outside your control or reused in multiple places.

Can’t We Just Overload Methods in Python?

You might think of using method overloading, defining multiple methods with the same name but different argument types — like in some statically typed languages. But in Python, if you define multiple methods with the same name, only the last definition takes effect. Any previous ones are silently overridden.

So, what can we do?

The Solution: singledispatchmethod

Fortunately, Python offers a neat solution via the functools module: the singledispatchmethod decorator (and its function counterpart, singledispatch). This lets you define one generic method and then register different implementations for different argument types — just like overloading.

Here’s how it works using our recycling plant example:

Output:

What Did We Achieve?

Using this method, we can easily add and process new waste types by:

  • Creating a new waste class
  • Adding a new @process.register(NewType) method to the RecyclingPlant class

All this without changing the existing process() call in the rest of the program. That’s real flexibility, and it follows the Open/Closed Principle in practice.

This means that in Python, we can achieve function or method overloading behavior based on the type of the first argument, thanks to singledispatch and singledispatchmethod.

For a deeper dive into this topic, check out the chapter Multiple functions or methods with the same name but different parameter types in the e-book Python Knowledge Building Step by Step.

Interested in the e-book Python Knowledge Building Step by Step: From the Basics to Your First Desktop Application?