Why do I need singledispatchmethod?

As mentioned in a previous post about singledispatch, other programming languages have native method or function overloading. For example, in Java one can make a class with methods that run different code depending on the datatype of the input.

With singledispatch one can run a different function when running my_func(<int>) vs my_func(<str>).

That's the main idea; see the other post about singledispatch for more examples.

Regarding singledispatchmethod, it's the same idea as singledispatch except to be used as a class method decorator rather than a function decorator.

Note: singledispatchmethod was added in Python 3.8. It makes sense that singledispatch was difficult to implement as a method decorator prior to Python 3.8 in that singledispatch works on the first element of a function, and the first element of a method refers to self or the instance of the object (i.e. not the first method argument). There are some workarounds, but they're quite ugly1 and it would be recommended to merely upgrade to Python 3.8.

Example without singledispatchmethod

Building upon the example from the the other post about singledispatch, we are now creating a class to print currency values.

class Currency:

    def __init__(self, units, revenue):
        self.units = units
        self.revenue = revenue

    def print_currency(self, val):
        if isinstance(val, float):
            return '$' + str(val)
        else:
            return str(val)


units = 10
revenue = 100.0
amount = Currency(units, revenue)
output = f'We sold {amount.print_currency(amount.units)} units for {amount.print_currency(amount.revenue)}.'
print(output)

>>> 'We sold 10 units for $100.0.'

Example with singledispatchmethod

from functools import singledispatchmethod
class Currency:

    def __init__(self, units, revenue):
        self.units = units
        self.revenue = revenue

    @singledispatchmethod
    def print_currency(self, val):
        raise NotImplementedError("Incorrect type for print_currency")

    @print_currency.register
    def _(self, val:float):
        return '$' + str(val)

    @print_currency.register
    def _(self, val:int):
        return str(val)


units = 10
revenue = 100.0
amount = Currency(units, revenue)
output = f'We sold {amount.print_currency(amount.units)} units for {amount.print_currency(amount.revenue)}.'
print(output)


>>> 'We sold 10 units for $100.0.'

I feel that even though the example with singledispatchmethod adds many more lines to the code, it's much more readable and makes more sense quicker than the first example. Without using singledispatchmethod I need to look through a group of nested if/else statements and understand what is trying to be done. singledispatchmethod is specifically used to dispatch to different types, so the reasoning is clear.

Conclusion

By using singledispatchmethod in your code you can make your code more readable and modular if you require a concept similar to function overloading within class definitions.

Comments

comments powered by Disqus

Published

Category

Python

Tags

Contact