Reading code should be easy, not hard. In my Code Review Quick Wins post, I introduced some strategies to help you write maintainable code and make our code reviews more effective. One section of that post touched on the topic of code complexity, where we discussed using a dictionary to streamline a nested if-statement. Today, I want to dive deeper into this powerful technique and show how we can use dictionaries to create clear, concise function lookups.
Dispatch Dictionaries
We read code far more often than we write it, so it’s crucial that our code is easy to understand. When dealing with multiple conditions that each trigger different actions or outcomes, a flat dictionary-based approach can be a lot more readable and maintainable than a nest of if-statements. Let’s look at an example of some deeply nested code, and then see how we can improve it with a dictionary.
Here’s the initial code:
ORDER_NEEDS_ATTENTION = "ORDER_NEEDS_ATTENTION"
NEW_ORDER = "NEW_ORDER"
MAKING_ORDER = "MAKING_ORDER"
ORDER_OUT_FOR_DELIVERY = "ORDER_OUT_FOR_DELIVERY"
ORDER_DELIVERED = "ORDER_DELIVERED"
ORDER_PROCESSING = "ORDER_PROCESSING"
def get_cookie_order_status(order):
if order.status == ORDER_NEEDS_ATTENTION:
txt_msg = "Your order needs attention. Please contact us at 555-555-5555"
elif order.status == NEW_ORDER:
txt_msg = "Order received! We will alert you to future updates!"
elif order.status == MAKING_ORDER:
txt_msg = "Your cookies are being prepared! They smell delicious!"
elif order.status == ORDER_OUT_FOR_DELIVERY:
if not order.paid:
txt_msg = "Your order is out for delivery. Please be sure to have your payment ready!"
else:
txt_msg = "Your order is out for delivery. Thanks for your business!"
elif order.status == ORDER_DELIVERED:
if order.driver.received_tip:
txt_msg = "Your order was delivered! Have a sweet day!"
else:
txt_msg = (
"Your order was delivered! Please "
"check back in the app to rate and tip your driver!"
)
elif order.status == ORDER_PROCESSING:
txt_msg = "We are getting ready to start your order!"
else:
txt_msg = "Your order has no updates at this time. We will check in soon!"
return txt_msg
The issues with this code are clear: it’s difficult to figure out what each conditional does at a glance, the normal flow of code isn’t obvious, and adding new conditions can get messy.
Now, let’s see how we can refactor this code using a dictionary as a lookup table for functions:
ORDER_NEEDS_ATTENTION = "ORDER_NEEDS_ATTENTION"
NEW_ORDER = "NEW_ORDER"
MAKING_ORDER = "MAKING_ORDER"
ORDER_OUT_FOR_DELIVERY = "ORDER_OUT_FOR_DELIVERY"
ORDER_DELIVERED = "ORDER_DELIVERED"
ORDER_PROCESSING = "ORDER_PROCESSING"
def get_cookie_order_status(order):
cookie_order_status_dispatch = {
ORDER_NEEDS_ATTENTION: get_order_needs_attention_status,
NEW_ORDER: get_new_order_status,
MAKING_ORDER: get_making_order_status,
ORDER_OUT_FOR_DELIVERY: get_order_out_for_delivery_status,
ORDER_DELIVERED: get_order_delivered_status,
ORDER_PROCESSING: get_order_processing_status,
}
if order.status in cookie_order_status_dispatch:
return cookie_order_status_dispatch[order.status](order)
return get_default_order_status(order)
def get_order_needs_attention_status(order):
return "Your order needs attention. Please contact us at 555-555-5555"
def get_new_order_status(order):
return "Order received! We will alert you to future updates!"
def get_making_order_status(order):
return "Your cookies are being prepared! They smell delicious!"
def get_order_out_for_delivery_status(order):
if not order.paid:
return "Your order is out for delivery. Please be sure to have your payment ready!"
else:
return "Your order is out for delivery. Thanks for your business!"
def get_order_delivered_status(order):
if order.driver.received_tip:
return "Your order was delivered! Have a sweet day!"
else:
return (
"Your order was delivered! Please "
"check back in the app to rate and tip your driver!"
)
def get_order_processing_status(order):
return "We are getting ready to start your order!"
def get_default_order_status(order):
return "Your order has no updates at this time. We will check in soon!"
In this refactored version, we’ve used a dictionary to map each order status to a corresponding function or message. This approach not only reduces the length of our function for the sake of readability, but it also makes the function more maintainable. Adding, removing, or changing responses to order statuses becomes as easy as updating the dictionary and adding a new function. Please note, the helper functions above are pretty basic and just return a string for the sake of simplicity.
Conclusion
As the Zen of Python reminds us, “Flat is better than nested.” But the benefits of using dictionaries for function dispatch go beyond just improving readability. This approach has multiple advantages:
- It aligns with another Python principle: “There should be one– and preferably only one –obvious way to do it.” By consolidating our responses in a single dictionary, we create a clear, singular mechanism for determining the outcome based on an order’s status.
- It simplifies the process of maintaining and updating our code. When you need to add, remove, or change responses to order statuses, you can do so easily by updating the dictionary and adding or modifying the corresponding functions.
- It promotes separation of concerns by breaking down complex logic into smaller, more manageable functions. This can make our code easier to test and debug.
This dictionary-based strategy is just one of the many ways we can simplify and improve our Python code. I hope you find it useful and it helps clean up your code. Now, who’s hungry for cookies?!