Package: pymultidispatch
pymultimethodsProvides multiple dispatch or multimethods as described inhttps://en.wikipedia.org/wiki/Multiple_dispatchmultimethods can be used to effectively solve the Expression Problem
described inhttps://en.wikipedia.org/wiki/Expression_problemIt can find uses in a lot of cases. Some examples are given below, but the uses are not limited
to only thoseFunctions where we process inputs differently based on isinstance checks of one or more input argumentsThe 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 DispatchFunctions 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, etcMore 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:defhandle(payload):type,version=payload["type"],payload["version"]if("init",3)==(type,version):returnhandle_init_v3(payload)elif...defhandle_init_v3(payload):...Better that aboveHANDLERS={("init",3):handle_init_v3...}defhandle(payload):type,version=payload["type"],payload["version"]returnHANDLERS[(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 functionfrompymultidispatch.multimethodsimportmultimethod@multimethod(lambdapayload:(payload["type"],payload["version"]))defhandle_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 functionpass# 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 fileThis implementation of multimethods is inspired by the Clojure language's implementation of the conceptUsage:The same as mentioned aboveThe@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 thedispatch_key_gen_fnreturns
andy of the given keysThedispatch_keyis the result of calling<dispatch_key_gen_fn>of the@multimethoddecoratorDefault params: Default params can be used, but only in the default handler (i.e. the function decorated with@multimethoddecorator). The other registered handler uses the default params (if any) in the default handler that
was decorated with@multimethoddecorator. Any default params overwritten at the time of the call still takes
precedence just like regular python functions do