Skip to main content

Multimethods implementation in Python

Project description

pymultimethods

Provides multiple dispatch or multimethods as described in https://en.wikipedia.org/wiki/Multiple_dispatch

multimethods can be used to effectively solve the Expression Problem described in https://en.wikipedia.org/wiki/Expression_problem

It can find uses in a lot of cases. Some examples are given below, but the uses are not limited to only those

  • Functions where we process inputs differently based on isinstance checks of one or more input arguments
  • The basic case of processing differently based on the type of one instance is what is used in Object oriented programming based on the self parameter. This basic methodology is what is called Single Dispatch
  • Functions where we process inputs differently based on the type of a field.
  • Example: Different handler for different versions of payload based on a "version" field in a dict, etc

More important than those is the ability that multimethods provide of NOT having to change the dispatcher for every single type of 'new' type that we come up with.

Usage is best described with an example:

Let's say we handle a payload based on a version number and type in the dict.

payload = {
"type": "init"
"version": 3,
...
}

In the payload above we want to dispatch to a handler based on "type" and "version"

Typical two ways to do this are:

def handle(payload):
  type, version = payload["type"], payload["version"]
  if ("init", 3) == (type, version):
  return handle_init_v3(payload)
  elif ...

def handle_init_v3(payload):
...

Better that above

HANDLERS = {
("init", 3): handle_init_v3
...
}

def handle(payload):
    type, version = payload["type"], payload["version"]
    return HANDLERS[(type, version)](payload)

The above two suffer from the problem of having to change existing files when a new version or handler is added. What if we could do this?

# Register a handler function using a @multimethod decorator. Arg to the decorator
# is the dispatch key generation function. Body of the function decorated is
# the default handler to be called if a handler wasn't registered for the
# dispatch key generated by the dispatch key generation function

from pymultidispatch.multimethods import multimethod

@multimethod(lambda payload: (payload["type"], payload["version"]))
def handle_message(payload):
    # A optional default handler function to be called if a handler wasn't registered for the
    # dispatch key generated by the above dispatch key generation function
    pass

# In the same file, or any other file
@handle_message.register(("init", 3))
def _(payload):  # Can be of any name, but leave out a name and use _ as convention
    # payload handler for init v3
    ...

# In the same file or any other file
@handle_message.register(("init", 4))
def _(payload):
    # payload handler for init v4
    ...

# We can also have more 'keys' passed into register to handle cases where the handler is the same
@handle_message.register(("init", 5), ("init", 6))
def _(payload):
    # payload handler for init v5 and init v6
    ...

As can be seen from above, there is no need to change the dispatcher function or any other existing file

This implementation of multimethods is inspired by the Clojure language's implementation of the concept

Usage:

The same as mentioned above

The @multimethod(<dispatch_key_gen_fn>) decorator is used to define a multimethod dispatch function. In the example above, the dispatcher is based on the payload type and payload version. It should be noted that the assumptions is that whatever input is given to this function is the same that is given to the dispatched functions. The dispatched functions are defined on this multimethod using the <fn>.register(<key>) decorator, where <fn> is the function decorated using @multimethod() decorator and <key> is the key for which we are registering <fn> as a handler. Note: Additional keys can also be passed onto to <fn>.register() function as follows:

<fn>.register(<key1>, <key2>, <key3>)

This will register the function for all the given keys and invoke the function when the dispatch_key_gen_fn returns andy of the given keys

The dispatch_key is the result of calling <dispatch_key_gen_fn> of the @multimethod decorator

Default params: Default params can be used, but only in the default handler (i.e. the function decorated with @multimethod decorator). The other registered handler uses the default params (if any) in the default handler that was decorated with @multimethod decorator. Any default params overwritten at the time of the call still takes precedence just like regular python functions do

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymultidispatch-1.2.0.tar.gz (4.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymultidispatch-1.2.0-py3-none-any.whl (6.8 kB view details)

Uploaded Python 3

File details

Details for the file pymultidispatch-1.2.0.tar.gz.

File metadata

  • Download URL: pymultidispatch-1.2.0.tar.gz
  • Upload date:
  • Size: 4.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.3 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.0 CPython/3.6.5

File hashes

Hashes for pymultidispatch-1.2.0.tar.gz
Algorithm Hash digest
SHA256 038253415bbcedc863f8e2d46e9272c06708c3edc0aa2ea6bbf1a7548dd6d271
MD5 64de31c3d309ad785ba776e1426205a4
BLAKE2b-256 3758fef9435c6807ab4e2bc107d5bc70ac86208fc8dbd9fd84a147953e7267ed

See more details on using hashes here.

File details

Details for the file pymultidispatch-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: pymultidispatch-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 6.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.3 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.0 CPython/3.6.5

File hashes

Hashes for pymultidispatch-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36b5ba0c9bbcc4b253c893cb75719bd8034afcc04e3bcc8efe2c7180fbcbca74
MD5 4898795275db362aad18f95b6b4a80d5
BLAKE2b-256 e7d23b804a64e367243d503d10a416ff26febe6e1163b3484bc36ff1de001edb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page