Python Forum
Switch .. case .. dispatcher based on singledispatch
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Switch .. case .. dispatcher based on singledispatch
#1
The generic function mechanism (functools.singledispatch) allows us to select an action based on the type of the first argument of the action in the same way that polymorphism does with a class instance.

This post is a study on how we can use this existing mechanism to select an action based on the value of the argument, in the same way that switch ... case ... case ... statements do in other languages. For example, we would like to have a different action depending on a certain variable having the value 'foo', 'bar' or 'baz'. But the singledispatch mechanism cannot do this because these three values have the same type!

The solution here is to create a dictionary-like object named CaseDispatcher that creates a new type and a new instance of that type for each of the words 'foo', 'bar', 'baz'. Based on the value of our variable, we can get this instance from the CaseDispatcher and select the action by the type of this instance.

Without further explanations, see by yourself if this code is understandable

__version__ = '2020.02.02'

import functools

def _delegator(a, m, self):
    """Helper function for delegate_methods()"""
    return getattr(getattr(self, a), m)

def _delegate_methods(cls, attr_name, method_names):
    """Add properties to a class that delegate to a member of the instance
    
    This function is a helper to build the CaseDispatcher class
    """
    for m in method_names:
        f = functools.partial(_delegator, attr_name, m)
        setattr(cls, m, property(f))

class Missing:
    __slots__ = ('case',)
    def __init__(self, value):
        self.case = value
        
class SubDict(dict):
    def __missing__(self, key):
        return Missing(key)

class CaseDispatcher:
    """A dictionary-like storing unique instances of custom types.
    
    Usage:
        After
        
            s = CaseDispatcher(['foo', 'bar', 'baz'])
        
        s['foo'], s['bar'] and s['baz'] are all instances of newly
        created data types. These types can be used to create generic
        functions, for example:
        
        @singledispatch
        def greet(obj):
            print('Hi!')
            
        @greet.register(type(s['foo']))
        def _(obj):
            print('Hi (foo version)!')

        @greet.register(type(s['bar']))
        def _(obj):
            print('Hi (bar version)!')

        x = random.choice(['foo', 'bar', 'baz'])
        greet(s[x])

    """
    def __init__(self, keys=None):
        self.map = SubDict()
        if keys:
            self.add(keys)
        
    def add(self, keys):
        for x in keys:
            if x in self.map:
                continue
            self.map[x] = type('<{}>'.format(x), (),
                            {'case': x, '__slots__': ()})()

# add dictionary-like methods to the CaseDispatcher class.
_delegate_methods(CaseDispatcher, 'map',
    ('__getitem__', 'get', 'items', 'keys', 'values',
     '__contains__', '__iter__', '__len__', '__delitem__'))


if __name__ == '__main__':
        import random
        
        s = CaseDispatcher(['foo', 'bar', 'baz'])
        
        @functools.singledispatch
        def greet(obj):
            print('Hi!')
            
        @greet.register(type(s['foo']))
        def _(obj):
            print('Hi (foo version)!')

        @greet.register(type(s['bar']))
        def _(obj):
            print('Hi (bar version)!')

        for i in range(6):
            x = random.choice(['foo', 'bar', 'baz', 'qux'])
            print(x, 'chosen: ', end = '')
            greet(s[x])
Output:
qux chosen: Hi! foo chosen: Hi (foo version)! bar chosen: Hi (bar version)! foo chosen: Hi (foo version)! foo chosen: Hi (foo version)! baz chosen: Hi!
Reply


Messages In This Thread
Switch .. case .. dispatcher based on singledispatch - by Gribouillis - Feb-01-2020, 11:05 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Dispatcher pattern Gribouillis 1 1,317 Aug-07-2023, 09:04 AM
Last Post: Gribouillis
  Roshambo with only 1 if switch Clunk_Head 12 5,725 Jan-28-2019, 08:59 AM
Last Post: perfringo
  switch to python3 Skaperen 0 2,132 Jul-03-2018, 12:55 AM
Last Post: Skaperen

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020