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"