Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here.
Python doesn't have function overloading.
What if we have a function that we'd like to use with different arguments depending on the situation?
For example, we'd like to make a greet function that can be called with nothing or can be called with a single argument:
>>> greet()
Hello world
>>> greet("Trey")
Hello Trey
In some programming languages, we could define two functions with the same name to overload that function with two different behaviors, depending on the arguments that were passed to it.
def greet():
print("Hello world")
def greet(name):
print("Hello", name)
This doesn't work in Python:
>>> greet("Trey")
Hello Trey
>>> greet()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: greet() missing 1 required positional argument: 'name'
In Python, when the second function was defined, it completely overwrote the first function. This happens because defining a function in Python really defines a function object, and then points a variable name to that function object (remember everything is an object in Python).
So we're seeing the same behavior as if we had redefined a variable:
>>> x = 0
>>> x
0
>>> x
0
>>> x = 4
>>> x
4
How could we achieve something like function overloading? Well, it depends on what we're trying to accomplish.
If we want a function that can accept different numbers of arguments, we could make some arguments optional by declaring default values when we define our function:
def greet(name="world"):
print("Hello", name)
Default values are very widely used in the Python world:
>>> greet("Trey")
Hello Trey
>>> greet()
Hello world
Factory functions are another alternative to function overloading.
Class methods are a pretty common way to make a factory function in Python:
import json
class User:
def __init__(self, id, name, email):
self.id, self.name, self.email = id, name, email
@classmethod
def from_json(cls, json_string):
data = json.loads(json_string)
return cls(data["id"], data["name"], data["email"])
def __repr__(self):
return f"User({self.id!r}, {self.name!r}, {self.email!r})"
Class methods often act as a sort of alternative class initializer.
We could make a User object by calling this User class (as we normally would):
>>> user = User(4, "Trey", "[email protected]")
>>> user
User(4, 'Trey', '[email protected]')
But if we have some JSON data that represents a User object, we could use our from_json class method to turn that JSON data into a User object:
>>> user_json = '{"id": 123, "name": "Trey", "email": "[email protected]"}'
>>> user = User.from_json(user_json)
>>> user
User(123, 'Trey', '[email protected]')
Class methods are a common way to make factory functions, but they're not the only way.
Any function that simply wraps around another function or class can be thought of as a factory function.
For example, this user_from_json function could be thought of as a factory function:
import json
class User:
def __init__(self, id, name, email):
self.id, self.name, self.email = id, name, email
def __repr__(self):
return f"User({self.id!r}, {self.name!r}, {self.email!r})"
def user_from_json(json_string):
data = json.loads(json_string)
return User(data['id'], data['name'], data['email'])
In fact, I would actually prefer this factory function over a class method because decoding JSON data doesn't seem like the responsibility of a class that manages users.
If you really wanted to call different functions based on the arguments provided to your function, there are some options available in Python.
Python's functools module has a singledispatch decorator for making a function that accepts a single argument and which calls different helper functions based on the type of that argument.
Here's a stringify function that uses this singledispach decorator:
from functools import singledispatch
@singledispatch
def stringify(data):
raise NotImplementedError("Data type not supported for stringification")
@stringify.register(list)
def _stringify_list(data):
return "\n".join([
str(row)
for row in data
])
@stringify.register(dict)
def _stringify_dict(data):
return "\n".join([f"{key}: {value}" for key, value in data.items()])
When we pass a list to this stringify function, it calls the _stringify_list function:
>>> print(stringify([2, 1, 3, 4]))
2
1
3
4
But if we pass in a dictionary, it calls the _stringify_dict function:
>>> print(stringify({"cats": 1, "dogs": 3, "rabbits": 9}))
cats: 1
dogs: 3
rabbits: 9
And if we passed in a different type, like a set, it calls the first function, which raises an exception:
>>> stringify({2, 1, 3})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.11/functools.py", line 909, in
wrapper
return dispatch(args[0].__class__)(*args, **kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/trey/stringify.py", line 5, in stringify
raise NotImplementedError("Data type not supported for stringification")
NotImplementedError: Data type not supported for stringification
There are also third-party libraries that allow for multiple dispatch, which does something similar, but with multiple function arguments instead of just one. The multidispatch and multimethod libraries are two examples.
Python doesn't have function overloading though you can achieve something similar with third-party libraries.
When you're seeking a function overloading alternative, it's usually best to keep it simple. Typically, it's best to rely on default argument values or factory functions instead.
We don't learn by reading or watching. We learn by doing. That means writing Python code.
Practice this topic by working on these related Python exercises.
Python, like many programming languages, has functions. A function is a block of code you can call to run that code.
Python's functions have a lot of "wait I didn't know that" features. Functions can define default argument values, functions can be called with keyword arguments, and functions can be written to accept any number of arguments.
To track your progress on this Python Morsels topic trail, sign in or sign up.
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.