Skip to content

sanjP10/multimethod-dispatcher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multimethod-dispatcher

Tests and Release CodeQL Build and Publish

This module that enables the use of the same function name but with variable dispatch keys. This allows different object types or values to execute different functions but with the same name. Similar to Object Orientation where objects can have their own implementation of a function, this is the functional response to this. Similar to Clojure's Multimethod

This originates from Alex Bard's blog post

There are two annotations:

  • @multi evaluates the arguments to return a unique key which determines which method is called.
  • @method is used to declare how that unique key is handled

The following code is an example of having multiple dictionaries with common elements, but handling the equation differently.

from multimethod import method, multi

@multi
def area(shape):
    """Multimethod dispatch key"""
    return shape.get('type')


@method(area, 'rectangle')
def area(rectangle):
    """Get area of a rectangle"""
    return rectangle.get('width') * rectangle.get('height')


@method(area, 'circle')
def area(circle):
    """Get area of a circle"""
    return circle.get('radius') ** 2 * 3.14159

area({'type': 'circle', 'radius': 0.5}) # => 0.7853975
area({'type': 'rectangle', 'width': 5, 'height': 8}) # => 40

This example is a more complicated version, evaluating on type and has a optional parameter for date of birth

from datetime import datetime
from multimethod import method, multi


class Person:
    """Person object to hold name and dob"""
    def __init__(self, name, dob):
        self.name = name
        self.dob = datetime.strptime(dob, '%Y-%m-%d')

    def get_name(self):
        """Return name"""
        return self.name

    def get_dob_as_str(self):
        """Return dob as string"""
        return self.dob.strftime('%Y-%m-%d')

@multi
def get_name(obj, _=None):
    """Multimethod dispatch key"""
    return obj.__class__


@method(get_name, dict)
def get_name(obj, _=None):
    """Dictionary function for getting name and dob from dict"""
    return "{} is born on {}".format(obj.get('name'), obj.get('dob'))


@method(get_name, str)
def get_name(obj, dob):
    """Dictionary function for getting name and dob from string"""
    return "{} is born on {}".format(obj, dob)


@method(get_name, Person)
def get_name(obj, _=None):
    """Person object type for getting name and dob"""
    return "{} is born on {}".format(obj.get_name(), obj.get_dob_as_str())


@method(get_name)  # Default
def get_name(*args, **kwargs):  # pylint: disable=W0613
    """Default response for any other object type"""
    return "No name"


person = Person('Steve', '2019-06-01')
get_name(person) # => 'Steve is born on 2019-06-01'
get_name({'name': 'Tom', 'dob': '2019-06-01'}) # => 'Tom is born on 2019-06-01'
get_name('George', '2019-06-01') # => 'George is born on 2019-06-01'
get_name(2) # => "No name"