Search code examples
pythonif-statementswitch-statementdispatch

Best practices for Python if-elif-elif-elif when dispatching requests


I have 5 sets of request's categories defined as python dicts, for example:

category1 = {'type1', 'type2', 'type3'}
category2 = {'type4', 'type5'}
category3 = {'type6', 'type7', 'type8', 'type9'}
category4 = {'type10', 'type11'}
category5 = {'type12', 'type13', 'type14'}

And I need to handle requests using their category, for instance:

if request_type in category1:
    # process category1 request
    process_category1_request(...)
elif request_type in category2:
    # process category2 request
    process_category2_request(...)
elif...

and I need to dispatch a request using the request type to a different function to process it.

I already know there are ways of dispatching this requests in Python without the need of using if-elif, but my question is: what's the best way to do it while maintaining the code clean and simple?


Solution

  • If request_type can be present in more than one category, you could use a tuple to loop through them in priority order:

    categories = (
        (category1, dispatch1method), 
        (category2, dispatch2method),
        (category3, dispatch3method),
        (category4, dispatch4method),
        (category5, dispatch5method),
    )
    
    next(method for cat, method in categories if request_type in cat)(arguments)
    

    Otherwise use a dict() to map category types to dispatch methods instead; reusing the same tuple-of-tuples mapping above to build a dispatch:

    category_dispatch = {}
    for cat, dispatch in categories:
        category_dispatch.update(dict.fromkeys(cat.keys(), dispatch))
    

    Then just look up the request type on that:

    category_dispatch[request_type](arguments)
    

    A mapping lookup like that would be faster than a scan through the tuple, where we have to test against each category in turn until we find a match.

    In fact, the priority ordering can be maintained by reversing that same tuple structure like so:

    category_dispatch = {}
    for cat, dispatch in reversed(categories):
        category_dispatch.update(dict.fromkeys(cat.keys(), dispatch))
    

    since now the highest priority mapping for a given request_type key will be entered into the category_dispatch structure last. This will give you the fastest dispatch even if request types were present in multiple categories.

    Disadvantage is that if your category* mappings are dynamic (request types get added to and removed from different categories over time) you'd need to maintain the category_dispatch dict to reflect those changes too.