Vedang Manerikar

My corner on the Internet.

Switch Statements in Python

| Comments

Today I went through some old Python code and noticed this pattern through out:

messy-ifs.py
1
2
3
4
5
6
7
8
9
10
response, data = somefunc()
if response == "this":
    do_this_with(data)
elif response == "that":
    do_that_with(data)
elif response == "huh":
    duh(data)
# lots more elifs.
else:
    prevent_horrible_crash(data)

This code should ideally have been a switch-case, but Python does not support a switch statement. Proponents of OOP believe that switch is bad - second only to goto. This is not strictly true - both goto and switch can be used elegantly and with great effect. Goto, for example, is great for undoing stacked changes and switch’s fall-through behavior allows nicely for ‘do things according to the stage I’m at’. However, if you’re doing OOP, consider using polymorphism instead.

I’m not doing OOP, so polymorphism does not apply to me, but I was looking for a way to optimize this code. I was on a code-cleanup spree, in the “flow”, and I thought, “Why don’t I use maps to do this?”. Brilliant! It was a really neat idea. Later I found out that this is the accepted way of doing switch-case in Python and I’d done nothing special. So anyway, the code now looked like this:

maps-as-switch.py
1
2
3
4
response_map = {"this": do_this_with,
                "that": do_that_with,
                "huh": duh}
response_map.get(response, prevent_horrible_crash)(data)

And that would have been that, had I not suddenly developed a conscience. I had replaced perfectly working, mostly readable code with some other code. What if my map solution was slower? What if it was much slower? I’d done a sizable amount of refactoring, and I did not relish throwing it away. I needed to test my solution, so I wrote some sample code:

The results were disheartening. My replacement code was slower - though only just.

1
2
3
$ python switch-speed.py
if - 2.08906793594
map - 2.88215684891

I tweaked the code a little and moved the creation of the map outside of the switch_map function. Python can access local variables faster than it can access global variables, but I figured a global dict would still beat the cost of creating the dict every time.

1
2
3
$ python switch-speed.py
if -  2.08955693245
map -  2.00381493568

I breathed a sigh of relief. There was a lesson to be learnt here - test/profile before you optimize!

References and articles:

Comments