Python: To Switch or not to Switch

It is easy to fall into the trap of writing switch like statements in any programing language. (switch is a construct in C and C++; in python it can be replicated by a string of elif statements.) Often, for example, this occurs at the interface between applications and libraries. In Python these look something like the following frivolous example:

def myswitch(x):
    if x == "one":
        return [1]
    elif x == "two" :
        return [1,2]
    elif x == "three" :
        return [1,2,3]
    else:
        print "Unknown  x"

This can work well enough, although in the example above, the default case, that is, the final else statement, probably won't produce the output expected: it will print an error message and implicitly return None.

Replacing the Switch by a Dictionary

An alternative approach is to translate the switch statement to a more data-orientated structure. For example, one could use the following code:

_swdict= { "one"   : [1],
           "two"   : [1,2],
           "three" : [1,2,3] }

def myswitch_v2(x):
    return _swdict[x]

The primary advantage of this approach is that the possible choices to the switch statement are cleanly exposed to the user of your program. For example, adding a function:

def myswitch_options():
    """Returns parameters accepted by myswitch"""
    return _swdict.keys()

allows the user to easily see what the valid values that can be passed to the myswitch function are. If you are building a graphical user interface to the routine, you could call myswitch_options and populate a drop box with its output.

The other main advantage is that this design allows you to easily add further choices for the switch statement while the program is running.

References vs Copy

The design above does, however, one common flaw: the lists returned by myswitch_v2 are returned as references, and the user may change them, thus changing the output of the future calls to the function. For example:

>>> print myswitch_v2("two")
[1, 2]
>>> myswitch_v2("two").append(3)
>>> print myswitch_v2("two")
[1, 2, 3]

If you want to ensure that the user can not make such a change, it is necessary to explicitly return a copy of the list held in the dictionary:

def myswitch_v3(x):
    return list(_swdict[x])

Functions in dictionaries

Although examples as simple as the one shown above occur often in Python programs, sometimes the switch pattern does more then just return a constant value. For example, here is a revised but equally frivolous example:

def fswitch(x):
    if x == "one":
        return x + "(1) =" + str([1])
    elif x == "two" :
        return x + "(2) =" + str([1,2])
    elif x == "three" :
        return x + "(3) =" + str([1,2,3])

Here, the switch statement returns a combination of the input parameter and constant values. This certainly does not preclude the dictionary approach:

_fdict= { "one"   : lambda x : x + "(1) =" + str([1]),
          "two"   : lambda x : x + "(2) =" + str([1,2]),
          "three" : lambda x : x + "(3) =" + str([1,2,3]) }

def fswitch_v2(x):
    return _fdict[x](x)

In other words, the dictionary can just as easily store functions as constants, and if the functions are short, they may be written in the simple lambda syntax.