Switch-case statement in Python revisited

This post is part of the Powerful Python series where I talk about features of the Python language that make the programmer’s job easier. The Powerful Python page contains links to more articles as well as a list of future articles.

About nine months ago I wrote a post talking about how you might go about implementing a switch case statement in the Python programming language. Python lacks a native switch-case like construct, but there are multiple ways to fake a similar effect. The most naive way would be multiple if-else blocks. A more Pythonic way would be to use Python’s dictionaries and and first class functions. A simple example in C, and the two Python ways is shown below.

switch(n) {
  case 0:
    printf("You typed zero.\n");
    break;
  case 1:
  case 9:
    printf("n is a perfect square\n");
    break;
  case 2:
    printf("n is an even number\n");
  case 3:
  case 5:
  case 7:
    printf("n is a prime number\n");
    break;
  case 4:
    printf("n is a perfect square\n");
  case 6:
  case 8:
    printf("n is an even number\n");
    break;
  default:
    printf("Only single-digit numbers are allowed\n");
  break;
}

if n == 0:
    print "You typed zero.\n"
elif n== 1 or n == 9 or n == 4:
    print "n is a perfect square\n"
elif n == 2:
    print "n is an even number\n"
elif  n== 3 or n == 5 or n == 7:
    print "n is a prime number\n"

options = {0 : zero,
                1 : sqr,
                4 : sqr,
                9 : sqr,
                2 : even,
                3 : prime,
                5 : prime,
                7 : prime,
}

def zero():
    print "You typed zero.\n"

def sqr():
    print "n is a perfect square\n"

def even():
    print "n is an even number\n"

def prime():
    print "n is a prime number\n"

The Fine Print

However, as the comments in the original post show, neither of the Python examples are a very good solution. They lack the versatility and power of the original C form, both in terms of syntax and semantics. Syntactically, neither of the forms do a good job of conveying the intent of the written code. The if-else form does an acceptable job and may be fewer lines, but it clutters the code with keywords and multiple equality checks and boolean combinations. The dictionary form is even worse at conveying the intention of the code.

Semantically, the two forms don’t stand up to the C form either. Multiple if-elses are probably as close as you can get, but it doesn’t work quite the same way. In particular, the ‘fall through’ semantics of a switch-case statement, where consecutive blocks are executed if there is no break statement, is a bit clumsy to duplicate. Repetitive code is almost always bad and trying to mimic the semantics of switch-cases for non-simple examples (including the above one) inevitably requires some repetition. Using dictionaries is simply a semantic mess. Creating a list and coming up with function names is really too much trouble for the simple task at hand. List comprehensions and dictionary comprehensions are powerful tools, but they simply have no place in something like a switch-case statement.

The Real Problem

This is one of the cases (pun unintended) where though you can use existing language features to get what you want (or something close), you would really like to have in-built language support. Python is a pretty well designed language as far as languages go, but it has it’s share of quirks. Personally I don’t consider a lack of switch case a particularly damaging lack, though there have been times where I wished there was one. In fact, a little Googling shows that there was a Python Enhancement Proposal submitted a few years ago but it was rejected due to lack of popular support.

There is an excellent Stack Overflow question on what the switch/case substitutes are and how to choose between them. The first answer to that question captures the essence of the problem at hand. It’s not a matter of how the alternative should be implemented, but rather what the alternative should mean. The if-else and dictionary lookups are generally useful if you have a simple choice to make in code which is mainly procedural. However if your code base is very object oriented, then you are probably better off with something like polymorphism to choose between alternatives. The solution should fit the problem, not the other way around.

The final thing I would like to say on this matter doesn’t involve Python at all. Rather it’s about Common Lisp, which I’ve been teaching myself for the last few weeks. The Lisp family of languages is particular famous (or infamous, depending on your point of view) because of the general lack of concrete syntax. Lisp code is written directly in the form of S-expressions. Essentially you write out the abstract syntax tree that in other languages is generated by the parsing the source code. Because the programmer has access to this representation, it’s possible to write code that will generate these S-expressions at compile time. In essence, this allows Lisp to be a programmable programming language. This is important because you as the programmer can basically add your own extensions to the language without waiting for a committee or community to approve it. In our case, if Common Lisp didn’t come with a switch-case statement and you really needed one, you could roll your own. In fact, it has been done. That’s not to say that rolling your language features is easy or something you should do on a daily basis, but in languages that allow it, it can be a very powerful tool if used right.

To be fair, I think you could write code to generate code in any run it in any language that has text processing and dynamic loading, but it would probably be very tedious and error-prone. The Lisp S-expression form lets you do it a much more elegant and powerful fashion.

In Conclusion

While Python does not have a switch case statement (and will probably never have one) there are a lot of other language features you can use to get the job done. It’s important to remember that you shouldn’t just be trying to recreate the semantics of switch-case (as that can be very messy). As the original post shows, trying to clone the C implementation is a futile endeavor. You need to pay attention to what the problem really is and then pick a Python feature that solves the problem correctly and elegantly. And if you get the chance, do explore languages like Lisp where syntax is fluid. It will help you better understand the difference between what your code looks like it’s doing and what it actually is doing. Happy hacking.