case/switch statement?

J

Joe Stevenson

Hi all,

I skimmed through the docs for Python, and I did not find anything like
a case or switch statement. I assume there is one and that I just
missed it. Can someone please point me to the appropriate document, or
post an example? I don't relish the idea especially long if-else
statements.

Joe
 
D

deelan

Joe said:
Hi all,

I skimmed through the docs for Python, and I did not find anything like
a case or switch statement. I assume there is one and that I just
missed it.

strictly speaking, python does not
have a switch stamenent.

"Why isn't there a switch or case statement in Python?"
Can someone please point me to the appropriate document, or
post an example? I don't relish the idea especially long if-else
statements.

topic already beated to death, feel
free to browse the group archives:
<http://groups-beta.google.com/groups?q=comp.lang.python+switch+statement>

HTH.
 
S

Steven D'Aprano

Hi all,

I skimmed through the docs for Python, and I did not find anything like
a case or switch statement. I assume there is one and that I just
missed it. Can someone please point me to the appropriate document, or
post an example? I don't relish the idea especially long if-else
statements.

I don't relish the idea of especially long case statements.

I've never understood why something like:

if x = 5:
do_this
elif x = 6:
do_that
else:
do_something_else

is supposed to be "bad", but

case of:
x = 5:
do_this
x = 6:
do_that
otherwise:
do_something_else

is supposed to be "good".

(Choose whatever syntax you prefer for case statements, the principle
remains.)

Arguably, a case statement *might* allow the compiler to optimize the
code, maybe, sometimes. But in general, no such optimization is possible,
so a case statement is merely equivalent to a series of if...elif...
statements.

There is no case statement in Python. If you don't care about
readability, one alternative is to use a dictionary:

case = {5: do_this, 6: do_that}
case.get(x, do_something_else)()
 
S

Skip Montanaro

Steven> I've never understood why something like:

Steven> if x = 5:
Steven> do_this
Steven> elif x = 6:
Steven> do_that
Steven> else:
Steven> do_something_else

Steven> is supposed to be "bad", but

Steven> case of:
Steven> x = 5:
Steven> do_this
Steven> x = 6:
Steven> do_that
Steven> otherwise:
Steven> do_something_else

Steven> is supposed to be "good".

...

Steven> Arguably, a case statement *might* allow the compiler to
Steven> optimize the code, maybe, sometimes.

If the case values are constants known to the compiler, it can generate O(1)
code to take the correct branch. (In fact, that could be done by the
compiler for if statements such as in your example today. It just isn't.)
It is precisely this behavior that is desired in many situations. See PEP
275 for details:

http://www.python.org/peps/pep-0275.html

Skip
 
T

Terry Hancock

I skimmed through the docs for Python, and I did not find anything like
a case or switch statement. I assume there is one and that I just
missed it. Can someone please point me to the appropriate document, or
post an example? I don't relish the idea especially long if-else
statements.
[...]
There is no case statement in Python. If you don't care about
readability, one alternative is to use a dictionary:

case = {5: do_this, 6: do_that}
case.get(x, do_something_else)()

The really nice thing about using dictionaries for this kind of thing
in Python is that what was previously hardcoded is now (dynamic)
data. That makes alterations like adding a new case extremely
trivial (you could load it from a config file for example, or it could
be altered by plugin code which simply updates the dictionary to
register itself). In my experience with C, this kind of change always
comes up with switch statements, so it's nice to neatly sidestep
the problem with dictionaries in Python.

And perhaps that removal of a false lead is the real reason Python
doesn't have a switch/case construct -- it encourages you to
use a smarter solution which you'll be glad of later on.
 
I

invalidemail

Philippe said:
I _love_ Python!


Well, if you loved that, here's something even more evil. This was
Bengt Richter's original idea that Cliff Wells and I improved upon.
First define some functions and classes:

.. _cache = {}
..
.. class _BaseCaseException(Exception):
.. pass
..
.. def switch(v):
.. if v not in _cache:
.. class _CaseException(_BaseCaseException):
.. pass
.. _cache[v] = _CaseException
.. raise _cache[v]
..
.. def case(v):
.. if v not in _cache:
.. class _CaseException(_BaseCaseException):
.. pass
.. _cache[v] = _CaseException
.. return _cache[v]
..
.. default = _BaseCaseException


Then you can define a switch statement like this:

.. x = 2
..
.. try: switch(x)
..
.. except case(1):
.. print "Case 1"
..
.. except case(2):
.. print "Case 2"
..
.. except case(3):
.. print "Case 3"
..
.. except default:
.. print "Default"
..
.. except NameError:
.. print "You can still catch regular exceptions like NameError"



I-love-Python-too-but-this-isn't-why-ly yr's,
 
S

Steven D'Aprano

If the case values are constants known to the compiler, it can generate O(1)
code to take the correct branch. (In fact, that could be done by the
compiler for if statements such as in your example today. It just isn't.)
It is precisely this behavior that is desired in many situations. See PEP
275 for details:

Agreed. I certainly don't object to sensible optimizations! But the number
of if...elif statements which can be optimised are a vanishingly small
subset of the number of possible if...elif statements.

And you can bet your last dollar that many people will use case statements
even when doing so gives no advantage, in a mistaken opinion that it will
be somehow magically faster.

In fact, some languages even encourage this: I recall the old 4GL (back in
the early 1990s when developers actually used the term Fourth Generation
Language unselfconsciously) used to develop the spreadsheet "Wingz". It
included two different forms for case. By memory:

case EXPR of:
VALUE:
SUITE
VALUE:
SUITE
otherwise:
SUITE

(which could potentially be optimised) and a second form:

case:
EXPR of:
SUITE
EXPR of:
SUITE
otherwise:
SUITE

which was almost certainly nothing more than syntactic sugar for:

if EXPR then:
SUITE
else if EXPR then:
SUITE
else:
SUITE

In any case, even when case statements can be optimized (and we all know
the lesson about premature optimization), the original poster's complaint
appeared to be purely a stylistic issue: he didn't relish using long
if..elif statements. Who does? Not I. My comment, I hope, will remind
folks that long pieces of branching code are ugly regardless of which
syntax you use.
 
D

Dan Sommers

I don't relish the idea of especially long case statements.
I've never understood why something like:
if x = 5:
do_this
elif x = 6:
do_that
else:
do_something_else
is supposed to be "bad", but
case of:
x = 5:
do_this
x = 6:
do_that
otherwise:
do_something_else
is supposed to be "good".

In the truly general case, I agree.

But twist your second example slightly into this:

case x of:
5: do_this
6: do_that
otherwise: do_something_else

and the goodness is obvious: we're choosing functionality based on the
value of x, so it's nice to see x in only one place.
Arguably, a case statement *might* allow the compiler to optimize the
code, maybe, sometimes. But in general, no such optimization is
possible, so a case statement is merely equivalent to a series of
if...elif... statements.

I agree that in general, optimizing a series of if/elif statements is
tricky, but your first example is very command and exactly the kind of
code that a good optimizer *can* optimize (as long as "x" isn't
pathological in the sense that evaluating it also changes its value or
has other side effects).
There is no case statement in Python. If you don't care about
readability, one alternative is to use a dictionary:
case = {5: do_this, 6: do_that}
case.get(x, do_something_else)()

I find this very readable. YMMV.

I also find this easier to extend in the "case" (pardon the pun) that my
program expands and x might now be 7 or 8 (or "foo" or 3j).

The biggest drawback here, as others have noted in previous discussions,
is that the do_* functions execute in a separate scope.

Regards,
Dan
 
S

Steven D'Aprano

In the truly general case, I agree.

But twist your second example slightly into this:

case x of:
5: do_this
6: do_that
otherwise: do_something_else

and the goodness is obvious: we're choosing functionality based on the
value of x, so it's nice to see x in only one place.

Yes. But now change the references to do_this and do_that to ten lines
of in-line code each, and add another dozen similar tests for 7, 8, etc,
and by the time you get to the third page you've forgotten what the
variable being tested is.

Now, you or I will obviously never write such hard-to-maintain code
<wink>, but some people will, and we'll have to maintain it.

It isn't at all obvious that case statements are more readable than
if...elif, nor are they necessarily faster at runtime, although they can
be. Against that occasional optimization and sometimes increase in
readability, you have disadvantages: more keywords, implicit tests instead
of explicit, new syntax to learn.


I agree that in general, optimizing a series of if/elif statements is
tricky, but your first example is very command and exactly the kind of
code that a good optimizer *can* optimize (as long as "x" isn't
pathological in the sense that evaluating it also changes its value or
has other side effects).

Yes. But that's also the sort of optimization that could be done for
if...elif as well, without introducing new syntax and new reserved words.

Case statements seem to be one of those things that Python newbies from
other languages automatically ask for, but the case for introducing case
(pun intended) doesn't appear to be strong enough to justify the amount of
verbiage spent on it.

And on that note, I will say no more on the subject.
 
P

Peter Hansen

Steven said:
Agreed. I certainly don't object to sensible optimizations! But the number
of if...elif statements which can be optimised are a vanishingly small
subset of the number of possible if...elif statements.

And you can bet your last dollar that many people will use case statements
even when doing so gives no advantage, in a mistaken opinion that it will
be somehow magically faster.

Case statements are actually more suitable in many cases even when
performance is not a goal for reasons of *readability*.

When reading an if statement, you have to scan down through effective
each statement attempting to find the case in which you are interested.
Some cases can be compound. The nested ifs can get confused with the
surrounding ones (in Python or a language with explicit block
delimiters). The cases are often not written in any particular order,
or they are ordered *for* performance reasons, but in ways that make it
harder to "scan".

A case statement, on the other hand, can be a message from the
programmer, saying in effect "here's code that represents a series of
_simple_ conditionals, ordered (usually) in a sensible way (e.g.
ascending numerical order), so just jump to the case you want and carry
on maintaining this nice code."

In current Python the equivalent approach is, of course, a dictionary of
some kind, though it's arguable whether this is as clean in many cases
as a case statement would be.

-Peter
 
C

Chinook

Case statements are actually more suitable in many cases even when
performance is not a goal for reasons of *readability*.

When reading an if statement, you have to scan down through effective
each statement attempting to find the case in which you are interested.
Some cases can be compound. The nested ifs can get confused with the
surrounding ones (in Python or a language with explicit block
delimiters). The cases are often not written in any particular order,
or they are ordered *for* performance reasons, but in ways that make it
harder to "scan".

A case statement, on the other hand, can be a message from the
programmer, saying in effect "here's code that represents a series of
_simple_ conditionals, ordered (usually) in a sensible way (e.g.
ascending numerical order), so just jump to the case you want and carry
on maintaining this nice code."

In current Python the equivalent approach is, of course, a dictionary of
some kind, though it's arguable whether this is as clean in many cases
as a case statement would be.

-Peter

I'm new to Python, but I'm retired from a software engineering career that
began in the early 60s. I'm not against case statements in the simplest
syntactical context, but over the years I've seen so many abuses of such
(i.e. difficult to follow code) that neither am I against their exclusion
from Python. The thought being (in my mind) that such might force the
programmer to rethink their approach to something more intuitively structured
(whether module/function wise or OO wise or some combination), though I admit
it is an idealistic view.

The problem I do see is your apples and oranges argument. You are equating
at the extreme, "compound" if conditions with "_simple_" case statement
conditionals. Yet you are leaving out of your argument the transition from
the former to the latter. If mapping one to the other is simple then the
readability is no harder with if statements. An example might be in dealing
with variably formated records where each record contains a record type flag.
On the other hand if you were to map compound conditions to "_simple_" case
statement conditionals, you need an intermediate translation that must be
studied to understand the "_simple_" conditionals anyway. Such translations
also promote an overabundance of status/flag variables to understand and
follow which does not increase the readability of code.

In my experience I've also seen where case statements promote overly long and
partially repetitive code blocks, which would be better constructed in a
top-down fashion. Admittedly, if statements could be used in a similar way
but I've not seen the same level of abuse with such.

So arguably, if the translation is pretty much one to one then the argument
is mute :~) If on the other hand the translation is many to one, then one's
skill in developing well structured readable code is really the issue, and
again case statements are a mute point.

Lee C
 
P

Peter Hansen

Chinook said:
The problem I do see is your apples and oranges argument. You are equating
at the extreme, "compound" if conditions with "_simple_" case statement
conditionals. Yet you are leaving out of your argument the transition from
the former to the latter. If mapping one to the other is simple then the
readability is no harder with if statements.

I dispute that, and believe you've misunderstood my core point. It's
not anything to do with "equivalence" between the two approaches. It's
that if you see a set of if/else statements, you have to look at all of
them to understand completely what's happening. (I mean the structure
of the if/else... the conditionals, not the contents of the blocks.)
With a case statement, on the other hand, you *know* that it must be
just simple conditionals (a series of x == some_constant tests), so you
don't need to look at all the cases, just the one that interests you.

So it's not a comparison between two ways of writing the same thing,
it's about the fact that with a case statement there are many things you
*cannot* write, so reading one is much easier than reading a similarly
sized compound if/else.

This is much like saying that a short function is easier to read than a
long one. The long one can obviously do much more, so it's an apples
and oranges comparison in that sense. But clearly if the short one fits
all on one screen and the long one does not, the short one is basically
much easier to grok.

That's all I'm saying.
In my experience I've also seen where case statements promote overly long and
partially repetitive code blocks, which would be better constructed in a
top-down fashion. Admittedly, if statements could be used in a similar way
but I've not seen the same level of abuse with such.

That's true.
So arguably, if the translation is pretty much one to one then the argument
is mute :~)
^^^^ "moot"

Sort of, except my point here would be that a case statement is then the
better choice in many cases because it communicates to the reader that
the entire thing *is* simple, while the if/else/if/else does not.

-Peter
 
S

Steve Holden

Peter said:
I dispute that, and believe you've misunderstood my core point. It's
not anything to do with "equivalence" between the two approaches. It's
that if you see a set of if/else statements, you have to look at all of
them to understand completely what's happening. (I mean the structure
of the if/else... the conditionals, not the contents of the blocks.)
With a case statement, on the other hand, you *know* that it must be
just simple conditionals (a series of x == some_constant tests), so you
don't need to look at all the cases, just the one that interests you.

So it's not a comparison between two ways of writing the same thing,
it's about the fact that with a case statement there are many things you
*cannot* write, so reading one is much easier than reading a similarly
sized compound if/else.
While this is how case statements *tend* to be used, it is of course
trivially possible to rewrite any if-then as a case:

case <condition>:
value True:
...
value False:
...

This would, of course, be a perversion of the purpose of the case
statement, and I agree with you that in *normal* usage the case
statement is easier to read because once you see the opening clause you
know a) that only one of the following cases will be executed, and b)
that control flow will resume after the case construct.

So despite my nitpicking I do agree with you that there's a margin of
readability that it might be useful to include in Python to avoid the
sometimes-lengthy if-elif-elif-elif-elif-elif strings one sometimes sees
- particularly so if any of the cases require conditionals, as nested
conditionals are probably among the more difficult sequences to read.
This is much like saying that a short function is easier to read than a
long one. The long one can obviously do much more, so it's an apples
and oranges comparison in that sense. But clearly if the short one fits
all on one screen and the long one does not, the short one is basically
much easier to grok.

That's all I'm saying.
And I'm agreeing.



That's true.
Indeed any language can be abused, but Python is more concerned with
making it easy to write good programs than difficult to write bad ones.
^^^^ "moot"
Well, if the argument never said anything perhaps it *was* mute too? :)
Sort of, except my point here would be that a case statement is then the
better choice in many cases because it communicates to the reader that
the entire thing *is* simple, while the if/else/if/else does not.

-Peter

Precisely.

regards
Steve
 
T

Terry Hancock

I find this very readable. YMMV.

Yeah, and I find this even more so:
case = {
5: do_this,
6: do_that,
}
case.get(x, do_default)()

Which is looking pretty close to a case statement, anyway.
 
S

Skip Montanaro

Terry> Yeah, and I find this even more so:

Terry> case = {
Terry> 5: do_this,
Terry> 6: do_that,
Terry> }
Terry> case.get(x, do_default)()

Terry> Which is looking pretty close to a case statement, anyway.

Sure, modulo namespace issues.

Skip
 
P

Philippe C. Martin

Any speed issue ?


Philippe said:
I _love_ Python!


Well, if you loved that, here's something even more evil. This was
Bengt Richter's original idea that Cliff Wells and I improved upon.
First define some functions and classes:

. _cache = {}
.
. class _BaseCaseException(Exception):
. pass
.
. def switch(v):
. if v not in _cache:
. class _CaseException(_BaseCaseException):
. pass
. _cache[v] = _CaseException
. raise _cache[v]
.
. def case(v):
. if v not in _cache:
. class _CaseException(_BaseCaseException):
. pass
. _cache[v] = _CaseException
. return _cache[v]
.
. default = _BaseCaseException


Then you can define a switch statement like this:

. x = 2
.
. try: switch(x)
.
. except case(1):
. print "Case 1"
.
. except case(2):
. print "Case 2"
.
. except case(3):
. print "Case 3"
.
. except default:
. print "Default"
.
. except NameError:
. print "You can still catch regular exceptions like NameError"



I-love-Python-too-but-this-isn't-why-ly yr's,
 
D

D H

Peter said:
With a case statement, on the other hand, you *know* that it must be
just simple conditionals (a series of x == some_constant tests), so you
don't need to look at all the cases, just the one that interests you.

Since you and Steve Holden agree that a case statement is useful, why
don't you propose it for python, or add it to the wiki page for Python 3000.

Here is the syntax the developers of your favorite language boo (
http://boo.codehaus.org/ ) are using:

given x:
when 1:
...
when 2:
...
otherwise:
...


must-stop-hyphenating-ly y'rs
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top