Switch Statement Emulation in Python
I’ve typically enjoyed having fall through switch statements in my programming languages. Not everyone loves em. They’re not always the right answer and they can be a bit dangerous; you miss a “break” and you “goto fail”. But they are easy to read and easy to understand – for me the switch statement is about readability (and to a lesser extent reducing the number of times you have to type the switched variable).
Python doesn’t have one, really. Guido has spoken. Still, lots of people like it and want some kind of equivalent functionality in Python. One popular replacement is dict-dispatch:
# have some functions called do_*
# parse the args. have an args.mode
{'signup': do_signup,
'login': do_login,
'logout': do_logout,
'cleanup': do_cleanup}.get(args.mode, default=do_default_stuff)()
We could make that a little more readable, but what’s the point? It doesn’t give us fall though. So I’m discarding dict-dispatch.
One of the best python switchy-replacements-with-fall-through ideas I came across was from the user Brian Beck at activestate. He made a switch function that returns an iterator that returns a function that can be called in a for loop. That reads badly, just see one of his example uses here:
# From http://code.activestate.com/recipes/410692/
import string
c = 'A'
for case in switch(c):
if case(*string.lowercase): # note the * for unpacking as arguments
print "c is lowercase!"
break
if case(*string.uppercase):
print "c is uppercase!"
break
if case('!', '?', '.'): # normal argument passing style also applies
print "c is a sentence terminator!"
break
if case(): # default
print "I dunno what c was!"
After reading his example, I created my own variation (that is used the same way) using a 1-tuple and a generator instance. Remember generator.send() is your friend. Anyway, here’s my switch function:
def switch(switch_value):
"""
Return a one-tuple containing a "case" function that can be used to
compare various things to the given switch_value
"""
# Partly inspired by Brian Beck's recipe 410692 at activestate.
# http://code.activestate.com/recipes/410692/
def fall_though_comparator(test_value):
"""
Yield True if any of the incoming values from generator.send()
match the given test_value. Always yield True after the first
match is found
"""
while True:
if test_value in (yield False):
while True: # start falling through
yield True
comparator = fall_though_comparator(switch_value)
comparator.next() # prime the comparator so it can accept a .send()
return (lambda *case_args: comparator.send(case_args), )
But now that it’s all done, I’m wondering if I actually prefer using this emulation to the alternative of just explicitly coding the equivalent logic in regular control structures. Here’s an image that contains an oversimple side-by-side example of identical code using the two different approaches.
(Click to enlarge)
I’ll have to think about it.