Hacker News new | ask | show | jobs
by hnlmorg 2180 days ago
Can someone explain to me the history behind Python's aversion to switch statements? I get Python is opinionated and I'm not trying to start a language war, it was just never clear to me why the `if ... elif` pattern was the preferred idiom.
7 comments

I came to Python after ~5 years of programming in C-style languages (about 15 years ago) and the lack of a switch statement may be the one thing about Python that demonstrably made me a better coder.

When I discovered there wasn't one, I was really annoyed and went digging for an explanation. The one I found was a suggestion if you are reaching for a switch statement, you're (probably) doing something wrong. This is not to impugn anyone else's approach or style and I will freely admit there are times when all you need is a switch and if/ else if/ else gets ugly, but most times I find the replacement for switch is not that but a dictionary holding a callable or similar. I recently showed this approach to a peer in PR and watching it click for her was awesome. She ripped out most of what she'd done and replaced some of our more ponderous permission checking with a dictionary of functions to apply.

I'd say the other thing I do when I wish I had a switch statement is realize I am writing code that is halfway to doing things The Right Way and refactor the block into smaller functions.

Can you give an example? I've never heard of this recommendation and I have a bit of trouble imagining it in a way that is simpler than a bunch of if-else

I came up with the following, and that definitely doesn't convince me.

    def call_a():
        pass

    def call_b():
        pass

    def default_func():
        pass

    myswitch = {
        "a": call_a
        "b": call_b
    }

    myfunc = myswitch.get(x, default_func)
    myfunc()
vs

    if x == "a":
        pass
    elif x == "b":
        pass
    else:
        pass
Taking this question from the opposite angle, what benefits do switch statements give us over if...elif?

Not actually a heavy Python user, but even though most languages I use regularly are more switch/case-heavy, I've never quite grasped why there's two largely interchangeable ways to do the one thing.

In lower level languages, when you use switch, you make it easier for the compiler to generate a jump table.

In higher level langues with strong, static types, the switch statement can indicate to the compiler you want to match on the type of the variable; it can do analysis then to make sure your pattern match is exhaustive.

These are good examples. Especially for a lower level languages this makes a lot of sense.

For higher level languages, I would imagine typeguards would provide some of the desired functionality here, while being much more lightweight than a full alternative conditional syntax.

Specifically to Ruby (therefore, this is not a generic answer), switch/case has syntax for "short form" expressions, that make the conditionals very coincise, by requiring only the variable part of what would otherwise be, repeating whole expressions.

Making an example is simpler than formulating a defition :-):

  case instance
  when MyClass...
  when MyOtherClass...
  ...
  end
which would otherwise be:

  if instance.is_a?(MyClass)
    ...
  elif instance.is_a?(MyOtherClass)
    ...
  ...
  end
if you consider that this has support for many other expressions (regular expressions, ranges...), you'll see a very coincise construct for the purpose.
Switch might "just" be a special case (har har) of if/elif, but it's a significant one: where you're branching only on the value of one variable, and branching on equality. I'd say it's significant enough to merit some special syntax.

To put it another way: when you're branching on the value of a single variable by comparing it to an enumerated set of values, the obvious way to do so is with switch/case.

> what benefits do switch statements give us over if...elif?

Clarity of intent, much as comprehensions provide over imperative loops (even though switch is still imperative in most langauges). Though whether it's enough benefit to be warranted is another question; I think basic switch is not clearly compelling, though adding even basic smarter matching (like Ruby has long had, for instance) makes it moreso.

Having both switch and if/elif allows writing code with less redundancy in it (unnecessary keywords and variables can be elided by choosing one selection construct over the other).
When you say "less redundancy", do you just mean terser code? Or what exactly?

I don't see any DRY trade-offs, unless you're talking on a really micro character-count / code-golf level.

I'm not averse to switch statements in general, but, in Python specifically, I just fail to see the point. In a higher-syntax language like C, a switch statement can save a lot of clutter, and also has some different semantics. In a language like ML, match statements come with a whole lot of extra static checking.

But Python's if/elif/else syntax is already clean enough that there's just not much clutter to remove. And C-style semantics on switch statements wouldn't be acceptable. And Python is a very dynamic language. So, in the end, you would end up with something that's generally the same line count and the same semantics as an equivalent if-statement, meaning it's would be more like semantic Splenda than semantic sugar.

I think culturally, because “explicit is better than implicit” (from the Zen of Python). Switch statements have a lot of implicit-ness to them (implicit invocation of equality comparison, to start) and it never seemed quite necessary, given how spare Python syntax is anyway.
I'd say the opposite: switch is explicitly about comparing a single variable against an enumerated set of possibilities, the equivalent if/elif construct has those same semantics only implicitly. The surface area of what switch/case means is very small and compact.
You choose to interpret a switch statement through an if statement, but that's arbitrary: the other way around is also possible (interpreting/rewriting an if-else construction as switch statements).
The point of GP is that the match assumes an equality test and it does it against a single object too.

There is no way in many languages to write an if using a match.

The article links to PEP 3103, which is a proposal to add a switch statement to the language. Interestingly, it was written by Guido himself, which you'd usually expect to give a pretty good chance of being accepted, especially since he was the sole decider of what would be accepted at the time! The rejection notice says simply:

> A quick poll during my keynote presentation at PyCon 2007 shows this proposal has no popular support. I therefore reject it.

[1] https://www.python.org/dev/peps/pep-3103/

I saw that but 'No popular support' doesn't really explain the 'why' part of why Python developers don't want a switch statement.
It's a remnant from the time when Python strived for simplicity and resisted adding syntax. Just like the decision against adding a ternary operator, etc. The alternative ways (dict of functions or if chain) were considered good enough weighed against the sin of adding relatively rarely used syntax.
Case statements are an anti-pattern. The proper way to implement that design pattern is using either dispatch dictionaries or plain-old polymorphism.
I see. Are you able to provide an example?
Suppose you are dispatching on token types. You could write something like this:

    handlers = {'start' : handle_start, 'jump' : handle_jump, 'end' : handle_end, ...}
    for tok, arg in tokens:
        if tok not in handlers:
            raise ValueError('Wrong token type, must be any of %s.' % ', '.join(handlers))
        handlers[tok](arg)
I see, I do follow that design pattern for larger switch statements or where the dictionary had to be dynamically populated (even in non-Python languages) but I hadn't considered using it for smaller switch statements as well.

Thank you for the example.