What is the proper way in Python to allow the user to extend the types on which a function can operate without altering the original code of the function?
Suppose I have a module with a
my_module.foo() function that was originally written to work on
float types. Now I would like the same function to be able to work also with, let’s say,
mpmath arbitrary precision floats – but without altering the code in the original module.
In C++ I would add an extra overload (or, more likely, some template specialisation trickery with an helper struct). How should I structure the original
my_module.foo() code so that the user can add her own custom hooks inside?
I can think of a few way to achieve this, but as a novice Python programmer I am sure most of them are going to be horrid 🙂
EDIT: thanks for all the answers so far, much appreciated.
I should probably clarify that one key requirement is being able to cope with types which I have not defined myself. E.g., if I am trying to code a generic
cos function in my module, I want to call
math.cos on builtin types,
sympy.cos on sympy symbolic types, etc. And of course I would like the dispatching logic not to be in my module’s
There are two ways to do this:
- Delegation. Already in Python. Most Pythonic. Doesn’t work for builtin types. Not exactly what you’re looking for.
- Single Dispatch. Still a PEP. Works for builtin types. Precisely what you’re looking for.
You usually delegate responsibility to the object you’re acting on, and you don’t implement the logic in your function.
Here’s an example:
len. The implementation of
len is very staightforward:
def len(obj): return obj.__len__()
Different types (
tuple…) have different implementations, but they all work with the same function.
Now, if I want to define my own type, that works with
len, I can do:
class MyTypeOfLength3(object): def __len__(self): return 3 o = MyTypeOfLength3() print len(o) # 3
In your case, you would be implementing something that looks like
(Note: this isn’t the actual code for
len, but it’s more or less equivalent.)
Of course, in some cases, that might not be practical. If that’s your case, then the “Single Dispatch” PEP 443 is probably what you’re looking for.
It suggests a new decorator that would accomplish what you’re looking for:
>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg) ... >>> @fun.register(int) ... def _(arg, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> @fun.register(list) ... def _(arg, verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
Once you’ve defined your function as such, you can call
fun(something), and Python will find out the correct implementation (
list here), of fallback to the default implementation
def fun(...): ....
You therefore solely have to decorate your original function, and you’re done, your users can add their own types.
Note: as pointed out in the comments,
singledispatch is already implemented in Python, it’s