Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 231
Sergio Ayestarán Avatar asked Jan 10 '13 17:01

Sergio Ayestarán


3 Answers

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.

like image 156
Martijn Pieters Avatar answered Oct 21 '22 07:10

Martijn Pieters


I think the cleanest may be two maps, to make the code most readable.

type_category_map = {"type1" : "category1", 
"type2" : "category1", , 
"type3" : "category1",
"type4" : "category2",
....
"type14" : "category5"}

category_function_map = {"category1" : "handler1_function",
"category2" : "handler2_function,
....
}

Then the python is just this:

category = type_category_map[request_type]
handler_function = category_function_map[category]
handler_function(request)

There would be ways to do it with a single data structure, but none that would be as clear and easy to follow as this, I think.

like image 32
Clay Wardell Avatar answered Oct 21 '22 07:10

Clay Wardell


Map your categories to a handler. Independent of the size of the map you will have O(1) access time.

MAP = {
  'cat1': handler1,
  'cat2': handler2,
   ....
}

MAP[request_type](...)
like image 40
Andreas Jung Avatar answered Oct 21 '22 08:10

Andreas Jung