3

I would like to dispatch Python functions dependent (e.g. using a dict approach) on the data type of the argument passed to the "dispatching" function (e.g. using isinstance()). Are there implementation alternatives? What's the easiest approach?

thinwybk
  • 4,193
  • 2
  • 40
  • 76
  • 1
    Did you try using the dictionary approach? What happened? – jonrsharpe Jul 24 '18 at 16:15
  • 4
    I *strongly* urge you to consider upgrading to Python 3, at which point the standard library offers you the [`@singledispatch` decorator function](https://docs.python.org/3/library/functools.html#functools.singledispatch) to take care of type-based dispatching. – Martijn Pieters Jul 24 '18 at 16:17
  • I know. But I have to use python-2.7 explicitly here. – thinwybk Jul 24 '18 at 16:23

2 Answers2

7

As of Python 3.4, the Python standard library includes support for @singledispatch() generic functions.

This lets you register multiple functions to handle different types, and it'll handle dispatch based on type, including subclass testing and caching. The method is described in PEP 443 - Single-dispatch generic functions.

There is a backport available on PyPI that supports Python 2.6 and up, written by the PEP author.

Note that Python 2.7 will soon be hitting a final end-of-life date, where it'll no longer receive bug fixes and security updates; you really need to plan for upgrading to Python 3 sooner rather than later. When you do, you'll note that the Python 3.7 version supports using type hinting to document what type each function accepts.

For example, a series of functions to remove None and False values from a nested dictionary-and-list data structure (a typical JSON data structure), can be defined as:

from functools import singledispatch

@singledispatch
def remove_null_false(ob):
    return ob

@remove_null_false.register
def _process_list(ob: list):
    return [remove_null_false(v) for v in ob]

@remove_null_false.register
def _process_list(ob: dict):
    return {k: remove_null_false(v) for k, v in ob.items()
            if v is not None and v is not True and v is not False}

In Python versions < 3.7 you'd have to move the type to the @remove_null_false.register(...) decorator factory notation.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

Please have a look at the below example.

def get_int_square(a):
    """
    Returns square of integer parameter
    """
    return a ** 2

def get_float_cube(a):
    """
    Returns cube of float parameter
    """
    return a ** 3

def sum_of_items(l):
    """
    Returns sum of all the items in list
    """
    return sum(l)

def get_squared_items(t):
    return tuple(item ** 2 for item in t)

def dispatching(a):
    """
    Calls the corresponding functions based on match found in the dictionary
    """
    functions = {
        'int': get_int_square,
        'float': get_float_cube,
        'list': sum_of_items,
        'tuple': get_squared_items
    }

    data_type = str(type(a)).split("'")[1]
    result = functions[data_type](a)
    return result

if __name__ == "__main__":
    print(dispatching(12))  # 144
    print(dispatching(1.2)) # 1.7279999999999998
    print(dispatching((4, 7, 9, 3, 1, 5, 8))) # (16, 49, 81, 9, 1, 25, 64)
    print(dispatching([56, 4, 50, 26, 24]))   # 160
hygull
  • 8,464
  • 2
  • 43
  • 52
  • Using own types instead of int, float, etc. should not be an issue. Cool. Thx. – thinwybk Jul 24 '18 at 17:25
  • You can use other types also, you just need to add more code lines. Please comment if you got any issue. Thanks for your reply. – hygull Jul 24 '18 at 18:17