To my knowledge, there are two types of overloading, one based on number of arguments, and one based on argument types
While overloading based on number of arguments has been covered here, I can't seem to find guidelines on function overloading by argument type.
So since type checking with type()
seems to be generally frowned upon, how do I do this in a pythonic way?
This seems less elegant than I would expect...
def overloaded_func(arg):
try:
do_list_action(arg)
except:
try:
do_dict_action(arg)
except:
pass
Type checking with
type()
(or, better,isinstance()
) isn't frowned upon in general in Python. What's frowned upon is using type as a proxy for behavior when you can get away with only checking the behavior. In other words, when you need an object to have some certain functionality, whereas in some other languages you would have to explicitly check whether its type supports that functionality, in Python you just assume the object does what you need it to do, and trust that an exception will be raised if that's not the case. But if you're choosing between different types of functionality, any of which will do the job for you, that's a different story.For example, suppose you have some code that can use either lists or dicts to implement integer-indexed arrays.
Perhaps it uses a dict if only a few, very large indices have values assigned, and a list otherwise. If you want to access the element at an index, if there is one, then you just write
self.store[index]
.You don't bother to check whether it's a list or a dict first, because the behavior you want - the ability to be indexed by an integer - exists either way.
But if you want to set the element at an index, if it's a list, you need to extend it to the proper length first. Now, proper duck typing would probably suggest you do this:
But I think most Python programmers would say
isinstance()
is better in this case. (No, really. It's okay.)I would generally recommend this route when you've only got a few types to test.
If you have many more types to test, it's more practical to use a dispatcher pattern, which is a functional approach. You build a mapping of types to functions that handle that type, and choose which one to call based on the type of the object you get. In this example, that would play out like this:
It's pretty silly to do this in this simple example, but in more complicated scenarios it can come in very handy. The pattern in general is
It even lets you dynamically add handlers for new types later on. This is basically what
functools.singledispatch
does (as another answer mentioned). That way just looks complicated because it hides thedispatch
dictionary as an attribute of the original function itself.It's impossible to say in general whether to use duck typing, type checking, dispatching, or something else, because it's somewhat subjective and depends on the details of your situation: how different is the code you need to handle different types? How many types are you dealing with? Do you need to be able to handle new types easily? And so on. You haven't given enough information in the question to allow anyone else to tell you which way seems best, but they all have their uses.
You could use
functools.singledispatch
. It will dispatch based on the type of first parameter and uses the default implementation if type doesn't match with any of the registered functions:Output:
Use
isinstance
:Alternatively, you might consider checking other ways such as for the presence of
__getitem__
or__iter__
etc. This depends on the details of why you're overloading, which you haven't shared with us.