switch

7 views
Skip to first unread message

hong zhang

unread,
Dec 8, 2009, 8:53:56 PM12/8/09
to pytho...@python.org
List,

Python does not have switch statement. Any other option does similar work?
Thanks for help.

--henry



Chris Rebert

unread,
Dec 8, 2009, 8:57:38 PM12/8/09
to hong zhang, pytho...@python.org
On Tue, Dec 8, 2009 at 5:53 PM, hong zhang <henryz...@yahoo.com> wrote:
> Python does not have switch statement. Any other option does similar work?

Yes, a dictionary with functions as values:
http://simonwillison.net/2004/May/7/switch/

Cheers,
Chris
--
http://blog.rebertia.com

Benjamin Kaplan

unread,
Dec 8, 2009, 9:00:59 PM12/8/09
to pytho...@python.org
On Tue, Dec 8, 2009 at 8:53 PM, hong zhang <henryz...@yahoo.com> wrote:
> List,

>
> Python does not have switch statement. Any other option does similar work?
> Thanks for help.
>


Use a dict instead, where the keys are the different cases and the
values are usually callable objects (such as functions)
options = {"a" : do_a, "b",do_b, "c", do_c}

option = "a"

try :
options[option]()
except KeyError :
do_default()

> --henry
>
>
>
> --
> http://mail.python.org/mailman/listinfo/python-list
>

rzzzwilson

unread,
Dec 8, 2009, 9:07:04 PM12/8/09
to
On Dec 9, 1:00 pm, Benjamin Kaplan <benjamin.kap...@case.edu> wrote:

Even better (well, shorter!):


options = {"a" : do_a, "b",do_b, "c", do_c}

options.get(option, do_default)()

Ross

zeph

unread,
Dec 8, 2009, 10:24:20 PM12/8/09
to
>
> Even better (well, shorter!):
> options = {"a" : do_a, "b",do_b, "c", do_c}
> options.get(option, do_default)()
>

You can also make it something callable like so, which is a little
more compact if you need to reuse it a lot:

>>> def do_a(x): print "a:", x
...
>>> def do_b(x): print "b:", x
...
>>> def do_c(x): print "c:", x
...
>>> do_something = {"a":do_a, "b":do_b, "c": do_c}.get
>>> do_something('a')(4)
a: 4
>>> do_something('c')(5)
c: 5
>>> do_something
<built-in method get of dict object at 0x6de40>
>>> do_something('d')
>>> do_something('d')(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Kee Nethery

unread,
Dec 9, 2009, 12:02:44 AM12/9/09
to pytho...@python.org
I string together a bunch of elif statements to simulate a switch

if foo == True:
blah
elif bar == True:
blah blah
elif bar == False:
blarg
elif ....

Asun Friere

unread,
Dec 9, 2009, 12:36:23 AM12/9/09
to


This code is probably symptomatic of poor design. (Not to mention that
your condition tests). For which reason python has no 'case'
statement and why no decent OO language should.

It is a principle of OO design that "an object should know what to do
itself." Rather running an object though a series of tests, it is
better to send the object a message, relying on polymorphism or duck-
typing, and deal with any exceptions thrown.

Generally if you find yourself wanting to use a 'case' statement or
writing a series of if/elif which involves more than say, three,
elifs, condsider whether you cannot use a <a href="http://
peak.telecommunity.com/protocol_ref/dispatch-example.html">double
dispatch</a> mechanism instead.

Steven D'Aprano

unread,
Dec 9, 2009, 1:12:15 AM12/9/09
to


Are you sure you want to test for equality with True and False? Generally
one should write that as:

if foo:
blah
elif bar:
blah blah
elif not bar:
blarg
...


--
Steven

Steven D'Aprano

unread,
Dec 9, 2009, 1:39:48 AM12/9/09
to
On Tue, 08 Dec 2009 21:36:23 -0800, Asun Friere wrote:

> On Dec 9, 4:02 pm, Kee Nethery <k...@kagi.com> wrote:
>> I string together a bunch of elif statements to simulate a switch
>>
>> if foo == True:
>>         blah
>> elif bar == True:
>>         blah blah
>> elif bar == False:
>>         blarg
>> elif ....
>
>
> This code is probably symptomatic of poor design. (Not to mention that
> your condition tests). For which reason python has no 'case' statement
> and why no decent OO language should.

That's a provocative statement.

> It is a principle of OO design that "an object should know what to do
> itself." Rather running an object though a series of tests, it is
> better to send the object a message, relying on polymorphism or duck-
> typing, and deal with any exceptions thrown.

Perhaps that's true, but you'll note that the example given above doesn't
run a single object through a series of tests, but runs a series of tests
on DIFFERENT objects, to find the first which matches.

But putting that aside, I find myself wondering how you would deal with
the following switch-like series of tests.


def print_grades(score):
if not 0 <= score <= 100:
raise ValueError("score must be between 0 and 100")
if score < 50:
print "You have failed."
consider_suspension()
elif score == 50:
print "You have just passed by the skin of your teeth."
elif score < 60:
print "You have scored a D. You need to try harder."
elif score < 70:
print "You have scored a C."
elif score < 80:
print "You have scored a B. Well done."
elif score < 100:
print "Congratulations, you have scored an A."
else:
assert score == 100
print "You have scored a PERFECT 100% SCORE!!!"
if not evidence_of_cheating():
call_newspapers()


Obviously that could, with a non-trivial amount of work, be turned into a
dictionary dispatch, but is the benefit worth the extra effort?


> Generally if you find yourself wanting to use a 'case' statement or
> writing a series of if/elif which involves more than say, three, elifs,
> condsider whether you cannot use a <a href="http://
> peak.telecommunity.com/protocol_ref/dispatch-example.html">double
> dispatch</a> mechanism instead.

I don't see how a series of tests on a single object is comparable to the
double-dispatch example given.

--
Steven

Asun Friere

unread,
Dec 9, 2009, 1:43:54 AM12/9/09
to
On Dec 9, 5:12 pm, Steven D'Aprano

I was going to point that out, but thought it a little OT. One might
also mention that testing for "if foo is None :" is a special case.
I'm also having a bit of a problem imagining what the subsequent
conditions must be which make testing "elif not bar" subsequent to
testing "elif bar" necessary, but that's just me.

Back OT, one would hope not to encounter python code with a long chain
of elifs like that. Probably the design should be improved, or where
this would be overkill, use the dictionary trick.

Carl Banks

unread,
Dec 9, 2009, 3:08:39 AM12/9/09
to
On Dec 8, 9:36 pm, Asun Friere <afri...@yahoo.co.uk> wrote:
> This code is probably symptomatic of poor design. (Not to mention that
> your condition tests).  For which reason python has no 'case'
> statement and why no decent OO language should.
>
> It is a principle of OO design that "an object should know what to do
> itself."  Rather running an object though a series of tests, it is
> better to send the object a message, relying on polymorphism or duck-
> typing, and deal with any exceptions thrown.

What if the object is a string you just read from a file?

How do you dispatch using polymorphism in that case?


Carl Banks

Bruno Desthuilliers

unread,
Dec 9, 2009, 3:54:22 AM12/9/09
to
Steven D'Aprano a écrit :

> On Tue, 08 Dec 2009 21:36:23 -0800, Asun Friere wrote:
(snip)

>> It is a principle of OO design that "an object should know what to do
>> itself." Rather running an object though a series of tests, it is
>> better to send the object a message, relying on polymorphism or duck-
>> typing, and deal with any exceptions thrown.

> But putting that aside, I find myself wondering how you would deal with

> the following switch-like series of tests.
>
>
> def print_grades(score):
> if not 0 <= score <= 100:
> raise ValueError("score must be between 0 and 100")
> if score < 50:
> print "You have failed."
> consider_suspension()
> elif score == 50:
> print "You have just passed by the skin of your teeth."
> elif score < 60:
> print "You have scored a D. You need to try harder."
> elif score < 70:
> print "You have scored a C."
> elif score < 80:
> print "You have scored a B. Well done."
> elif score < 100:
> print "Congratulations, you have scored an A."
> else:
> assert score == 100
> print "You have scored a PERFECT 100% SCORE!!!"
> if not evidence_of_cheating():
> call_newspapers()


Well, obviously such business rules must by no mean be hardcoded. You
really need a "rule engine", configurable by your domain experts thru a
DSL that we'll design specially for you. The rule engine will generate
an AbstractScoreFactory that will instanciate appropriate IScore
implementation objects that knows what to do.

You also need to decouple the output mechanism - what if you need to
output to a web page, an IPhone app, a RSS stream, an audio stream or
clay tablets ? To allow for maximum decoupling, the output mechanism
should be configurable thru a strict, well defined and universally
understood language - I mean XML, of course.

>
> Obviously that could, with a non-trivial amount of work, be turned into a
> dictionary dispatch,

Dictionnary dispatch ? C'mon, you must be joking ? An enterprise
application is not some Q&D cowboy script, you know ? You do have to
apply state of the art designs and patterns to do it properly

<g>


Matt McCredie

unread,
Dec 9, 2009, 1:02:21 PM12/9/09
to pytho...@python.org
hong zhang <henryzhang62 <at> yahoo.com> writes:

>
> List,
>
> Python does not have switch statement. Any other option does similar work?
> Thanks for help.
>

> --henry
>
>

I see a couple of people have mentioned using a dictionary. If the value that
you are switching on is a string, or could be made into one, you can use a
variant of the command dispatch pattern.


class MyCommandDispatcher(object):
def do_a(self):
# do stuff

def do_b(self):
# do stuff

def do_5(self):
# do stuff

def default(self):
# do stuff

def switch(self, option):
getattr(self, 'do_' + str(option), self.default)()


d = MyCommandDispatcher()
d.switch('a')
d.switch(5)


This isn't _much_ more coding than using the dictionary method, and is pretty
readable. This is also a common pattern in python.


Matt

Nobody

unread,
Dec 9, 2009, 1:50:29 PM12/9/09
to
On Tue, 08 Dec 2009 21:02:44 -0800, Kee Nethery wrote:

This isn't what would normally be considered a switch (i.e. what C
considers a switch). A switch tests the value of an expression against a
set of constants. If you were writing the above in C, you would need to
use a chain of if/else statements; you couldn't use a switch.

Compiled languages' switch statements typically require constant labels as
this enables various optimisations.

The above construct is equivalent to Lisp's "cond", or guards in some
functional languages.

While switch-like constructs can be implemented with a dictionary,
cond-like constructs would have to be implemented with a list, as there's
no guarantee that the tests are mutually exclusive, so the order is
significant. E.g.

rules = [((lambda (foo, bar): return foo), (lambda: blah)),
((lambda (foo, bar): return bar), (lambda: blah blah)),
((lambda (foo, bar): return not bar), (lambda: blarg)),
...]

for test, action in rules:
if test(foo, bar):
action()
break

Tim Chase

unread,
Dec 9, 2009, 2:57:48 PM12/9/09
to Carl Banks, pytho...@python.org
Carl Banks wrote:
> What if the object is a string you just read from a file?
>
> How do you dispatch using polymorphism in that case?

This is where I most miss a switch/case statement in Python...I
do lots of text-file processing (cellular provider data), so I
have lots of code (for each provider's individual format) that
looks like

phones = {}
for row in csv.DictReader(file('data.txt', 'rb')):
phonenumber = row['phonenumber']
if phonenumber not in phones:
phones[phonenumber] = Phone(phonenumber)
phone = phones[phonenumber]
rectype = rectype
if rectype == '01':
phone.international += Decimal(row['internationalcost'])
elif rectype == '02':
phone.text_messaging += (
int(row['textmessages sent']) +
int(row['pages received']) +
int(row['textmessages sent']) +
int(row['pages received'])
elif rectype == ...
...
else:
raise WhatTheHeckIsThis()

which would nicely change into something like

switch row['recordtype']:
case '01':
phone.international += Decimal(row['internationalcost'])
// optionally a "break" here depending on
// C/C++/Java/PHP syntax vs. Pascal syntax which
// doesn't have fall-through
case '02':
phone.text_messaging += (
int(row['textmessages sent']) +
int(row['pages received']) +
int(row['textmessages sent']) +
int(row['pages received'])
...
default:
raise WhatTheHeckIsThis()

This doesn't convert well (i.e. compactly) to a
dictionary-dispatch idiom. :(

-tkc


MRAB

unread,
Dec 9, 2009, 3:13:30 PM12/9/09
to pytho...@python.org
Tim Chase wrote:

> Carl Banks wrote:
>> What if the object is a string you just read from a file?
>>
>> How do you dispatch using polymorphism in that case?
>
[snip]

>
> which would nicely change into something like
>
> switch row['recordtype']:
> case '01':
> phone.international += Decimal(row['internationalcost'])
> // optionally a "break" here depending on
> // C/C++/Java/PHP syntax vs. Pascal syntax which
> // doesn't have fall-through
> case '02':
> phone.text_messaging += (
> int(row['textmessages sent']) +
> int(row['pages received']) +
> int(row['textmessages sent']) +
> int(row['pages received'])
> ...
> default:
> raise WhatTheHeckIsThis()
>
> This doesn't convert well (i.e. compactly) to a dictionary-dispatch
> idiom. :(
>
Shouldn't 'case' be indented to the same level as 'switch'? And
'default' could be replaced by 'else' without ambiguity.

Tim Chase

unread,
Dec 9, 2009, 4:09:29 PM12/9/09
to MRAB, pytho...@python.org
MRAB wrote:

> Tim Chase wrote:
>> switch row['recordtype']:
>> case '01':
>> phone.international += Decimal(row['internationalcost'])
>> // optionally a "break" here depending on
>> // C/C++/Java/PHP syntax vs. Pascal syntax which
>> // doesn't have fall-through
>> case '02':
>> phone.text_messaging += (
>> int(row['textmessages sent']) +
>> int(row['pages received']) +
>> int(row['textmessages sent']) +
>> int(row['pages received'])
>> ...
>> default:
>> raise WhatTheHeckIsThis()
>>
>> This doesn't convert well (i.e. compactly) to a dictionary-dispatch
>> idiom. :(
>>
> Shouldn't 'case' be indented to the same level as 'switch'? And
> 'default' could be replaced by 'else' without ambiguity.

But I want a GREEN bike-shed! :-) Yeah, "else" works nicely and
makes sense. Indentation could go either way in my book, but I
lean towards indented "case" because the "switch" can get easily
lost if the "case"s aren't indented:

switch foo:
case 1:
stuff()
case 2:
morestuff()
switch bar:
case 3:
whatever()
case 4:
yet_more()
else:
whip_it()

vs

switch foo:
case 1:
stuff()
case 2:
morestuff()
switch bar:
case 3:
whatever()
case 4:
yet_more()
else:
whip_it()

Just my ponderings...

-tkc

Asun Friere

unread,
Dec 9, 2009, 7:45:42 PM12/9/09
to

On Dec 9, 5:39 pm, Steven D'Aprano

<ste...@REMOVE.THIS.cybersource.com.au> wrote:
> On Tue, 08 Dec 2009 21:36:23 -0800, Asun Friere wrote:
> > On Dec 9, 4:02 pm, Kee Nethery <k...@kagi.com> wrote:
> >> I string together a bunch of elif statements to simulate a switch
>
> >> if foo == True:
> >>         blah
> >> elif bar == True:
> >>         blah blah
> >> elif bar == False:
> >>         blarg
> >> elif ....
>
> > This code is probably symptomatic of poor design. (Not to mention that
> > your condition tests).  For which reason python has no 'case' statement
> > and why no decent OO language should.
>
> That's a provocative statement.
>

My reply was lost in the aether, so here goes again.

If it's provocative, it is at least hedged. It is merely symptomatic
and only probably so, because there are numerous instances where case
logic is the only sensible solution. I'm not advocating some cargo-
cult rule for the elimination of all uses of elif. If I were I would
rightly be presented with numerous code examples where a switch is a
sensible option, much as happens when someone pronounces against the
humble goto statement.


> > It is a principle of OO design that "an object should know what to do
> > itself."  Rather running an object though a series of tests, it is
> > better to send the object a message, relying on polymorphism or duck-
> > typing, and deal with any exceptions thrown.
>
> Perhaps that's true, but you'll note that the example given above doesn't
> run a single object through a series of tests, but runs a series of tests
> on DIFFERENT objects, to find the first which matches.
>

Well actually two objects with one being tested twice. But you are
right, I was being sloppy when I wrote "running an object" especially
in light of the fact that the following clause makes more sense when
run against objects of potentially different class. Same for dispatch
mechanisms of course.

What I'm saying is that when you find a large if/elif/else in your
code, regard it with suspicion and "consider" whether better design
might not eliminate it. And I'm speaking as someone who still has to
maintain some code (in perl not python) which has an if/elif/else
statement spanning 5 A4 pages. What's worse, I was the one who did
this to myself some 8 years ago.

What I'm also saying is "learn about dispatch mechanisms," they are
about the most useful patterns out there (next to the State pattern).
As a matter of practice I have found that more often than not, large
case statements can better be solved using double-dispatch. Obviously
not all. Obviously!

Probably not. Depending on the nature of the app, I'd probably be
calling score.print_grades() and using cutoff values of 85, 75, 60 and
50 (perhaps not even hardcoded into the logic), but sure this is a
fine example of a place where a solution other than a simple switch
would be overkill. As such this example would be a good counter to
the absolute repudiation of case logic I did not make. I doubt,
however, that it is of great pedagogic value in alerting programmers
to the design options available to them in overcomming what the
perceive as a lack in the language.

Asun Friere

unread,
Dec 9, 2009, 8:02:22 PM12/9/09
to
On Dec 9, 7:08 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
> What if the object is a string you just read from a file?
>
> How do you dispatch using polymorphism in that case?

This would be a pertinent question, were I advocating that _all_
switch statements should, or even can, be replaced with "dispatch
using polymorphism."

What if, instead of reading strings from a file, you are parsing, say
xml, into an object framework isomorphic to the file's schema? And
no, this is not a contrived example. Now you want to print out the
structure, or a branch thereof. To make matters interesting you want
to be able to print it out in a number of different formats. So we
have:

5 def print_out (element, fmnt) :
6 if element.__class__ is schema.Title :
7 if str(fmnt) == 'html' :
8 print_out_spam_title(element)
9 elif str(fmnt) == 'txt' :
10 print_out_ham_title(element)
11 elif ....
12 elif element.__class__ is schema.Paragraph :
13 if str(fmnt) == 'html' :
14 print_out_spam_paragraph(element)
15 elif str(fmnt) == 'txt' :
16 print_out_ham_paragraph(element)
17 elif ...
18 elif element.__class__ is ...
19 ...
20

And so on for a dozen or so tags and 3 formats. And imagine the joy
of adding the 4th or 5th format.

Now I guess you already realise that applying a dispatch mechanism
here will improve the design and result in code that is dryer, far
more easily extensible and arguably (but only arguably) more readible?

Carl Banks

unread,
Dec 9, 2009, 10:00:59 PM12/9/09
to
On Dec 9, 5:02 pm, Asun Friere <afri...@yahoo.co.uk> wrote:
> On Dec 9, 7:08 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
>
> > What if the object is a string you just read from a file?
>
> > How do you dispatch using polymorphism in that case?
>
> This would be a pertinent question, were I advocating that _all_
> switch statements should, or even can, be replaced with "dispatch
> using polymorphism."

Then why did you claim that a decent OO should never have a switch
statement then? You argued that a decent language OO should never
have a switch statement because polymorphic dispatch is the right way
to handle it in OO languages, which implies that polymorphism can and
should take the place of any switch statement.


> What if, instead of reading strings from a file,

Why don't you answer my question first, then I'll entertain whatever
point you are trying to make with this example?


Carl Banks

Steven D'Aprano

unread,
Dec 10, 2009, 12:47:19 AM12/10/09
to
On Wed, 09 Dec 2009 18:50:29 +0000, Nobody wrote:

> On Tue, 08 Dec 2009 21:02:44 -0800, Kee Nethery wrote:
>
>> I string together a bunch of elif statements to simulate a switch
>>
>> if foo == True:
>> blah
>> elif bar == True:
>> blah blah
>> elif bar == False:
>> blarg
>> elif ....
>
> This isn't what would normally be considered a switch (i.e. what C
> considers a switch).

Anyone would think that C was the only programming language in
existence...


> A switch tests the value of an expression against a
> set of constants.

In C. Things may be different in other languages.

For example, I recall the so-called "4GL" (remember when that was the
marketing term of choice for interpreted programming languages?)
Hyperscript from Informix. I can't check the exact syntax right now, but
it had a switch statement which allowed you to do either C-like tests
against a single expression, or if-like multiple independent tests.

Moving away from obsolete languages, we have Ruby which does much the
same thing: if you provide a test value, the case expression does a C-
like test against that expression, and if you don't, it does if-like
multiple tests.

http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-
you-can-do-with-it/

> If you were writing the above in C, you would need to
> use a chain of if/else statements; you couldn't use a switch.
>
> Compiled languages' switch statements typically require constant labels
> as this enables various optimisations.

Pascal, for example, can test against either single values, enumerated
values, or a range of values:

case n of
0:
writeln('zero');
1, 2:
writeln('one or two');
3...10:
writeln('something between three and ten');
else writeln('something different');
end;

--
Steven

Asun Friere

unread,
Dec 10, 2009, 6:34:53 AM12/10/09
to
On Dec 10, 2:00 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
> On Dec 9, 5:02 pm, Asun Friere <afri...@yahoo.co.uk> wrote:
>
> > On Dec 9, 7:08 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
>
> > > What if the object is a string you just read from a file?
>
> > > How do you dispatch using polymorphism in that case?
>
> > This would be a pertinent question, were I advocating that _all_
> > switch statements should, or even can, be replaced with "dispatch
> > using polymorphism."
>
> Then why did you claim that a decent OO should never have a switch
> statement then?

The mere fact that it is possible to demonstrate a use of the 'goto'
statement, which does not offend against a program's structural
integrity, does not mean it's necessarily a good idea to have it lying
about given that in the large majority of cases it leads to
(encourages?) bad code. Python sagely declined to implement 'goto'.

I feel the same considerations apply in regard to the switch/case
statement in the context of a true* OO (ie python but not java ;>).
Imo the decision not to implement a switch statement was wise. This
is not to deny that a traditional switch is not in often a sane
solution. I just think that if your elifs are getting unwieldy enough
to require the application of the proverbial layer of abstraction,
then a switch statement fails to deliver sufficient abstraction.

> You argued that a decent language OO should never
> have a switch statement because polymorphic dispatch is the right way
> to handle it in OO languages, which implies that polymorphism can and
> should take the place of any switch statement.

That is a misreading. I wrote, that the case logic is "probably"
symptomatic of poor design. I advocated "considering" whether an OO
solution might be more appropriate. You misunderstand my intent. I'm
not here to postulate some ultimate OO truths, but to provide
practical advice (aimed here mainly at Hong and Kee). In regard elif
chains, this is my advice.

If you see too many elifs in your code (or if you find yourself
wishing for a switch statement) alarm bells should start to go off.
STOP, give serious consideration as to whether applying some patterns
or similar will improve the design, and HINT: first consider whether
that pattern might be something resembling a dispatch mechanism, it's
just downright spooky how in real-life programming situations this
turns out to be the answer. Obviously the answers to these questions
are not invariably 'yes.' Often enough they are.

>
> > What if, instead of reading strings from a file,
>
> Why don't you answer my question first,

Because, as I have already explained, I object to the question. It
presupposes my holding a position I don't.

But look perhaps I'm being unfair to you Carl. I presumed your
question was rhetorical, or at least you supposed it disproved
whatever it is you were attacking. If instead you were asking for
clarification, the short answer is "wrap as you read." A longer
answer is in the response to Tim, I'll send in a while.

cheers

Asun Friere

unread,
Dec 10, 2009, 7:29:37 AM12/10/09
to

Great example Tim. This is something that many of us must be dealing
with on a daily basis. The problem has enough details (bar one), to
allow an answer and not so detailed as to be confusing. And for me
it's a particularly good example, because your need accommodate
mulitple provider formats makes me feel right at home.

> which would nicely change into something like
>
> switch row['recordtype']:
> case '01':
> phone.international += Decimal(row['internationalcost'])
> // optionally a "break" here depending on
> // C/C++/Java/PHP syntax vs. Pascal syntax which
> // doesn't have fall-through
> case '02':
> phone.text_messaging += (
> int(row['textmessages sent']) +
> int(row['pages received']) +
> int(row['textmessages sent']) +
> int(row['pages received'])
> ...
> default:
> raise WhatTheHeckIsThis()

Cleaner yes. But, with respect, not so clean as to justify the
construct. Following my advice you might express that switch
statement like so:

phone.update_from_record(record)

It is, in this context, completely irrelevant observe that 'dispatch'
originally referred specifically to the dismissal of ambassadors. It
may be slightly more to the point to tap into the wisdom of Henry Ford
and turn your design inside out.

This switch statement belongs to one guy. One guy who wants to know
how to do everything that needs to be done to Phones no matter who
asks. Let's install a conveyor belt instead!

Standard Disclaimer: Untested (obviously); just a sketch; I don't
really know your problem only the code you've posted; etc etc.

First some minimal "architecture":
---
#The first class your already have. We just need one more method

class Phone (object) :
...
def update_from_record (self, rec) :
return rec.updatePhone(self)


#and then some "data source" classes

class RecType (dict) :
def __init__ (self, row) :
...

class RecType_001 (RecType) :
def updatePhone(self, phone) :
phone.interational += Decimal(self['internationalcost'])

class RecType_002 (RecType) :
def updatePhone(self, phone) :
phone.text_messaging += (
int(self['textmessages sent']) +
int(self['pages received']) +
...

#and if we must ...
rectypes = {'01':RecType_001, '02': RecType_002, ...}

# The one thing I'm sure I don't understand from the code is where the
original rectypes comes into the process.
#I would prefer, if it's possible, just thowing the appropriate class
in rather than looking up this dict to instantiate.
---

Now the different providor types, previously the bane of your
existence, are your staff. Your original code will now read something
like:

phones = {}
for row in csv.DictReader(open('data.txt', 'rb')) :
try :
record = rectypes[rectype](row)
except KeyError :
raise WhatTheHeckIsThisError('unknown rectype: %s' % rectype)
phonenumber = record.phonenumber


if phonenumber not in phones :
phones[phonenumber] = Phone(phonenumber)
phone = phones[phonenumber]

phone.update_from_record(record)

I wonder if you agree that it's bit cleaner now? It's an effective
solution. I'm making no representation that it's the best.

I like think largely because it contains the knowledge accessibly. If
you have "lots of code" that deal with this kind of thing, chances are
library of core data source classage could reduce much of it to a
simple (and legible!) one liner. A provider "enhances" their format,
or a new provider format is added, code in one class, not in every
switch they might be involved in. But sorry, I don't mean to
patronise, I'm sure you know the spiel.

Asun

Carl Banks

unread,
Dec 10, 2009, 8:25:45 AM12/10/09
to
On Dec 10, 3:34 am, Asun Friere <afri...@yahoo.co.uk> wrote:
> On Dec 10, 2:00 pm, Carl Banks <pavlovevide...@gmail.com> wrote:

[snip most of questionable, verly verbose reply]

> > You argued that a decent language OO should never
> > have a switch statement because polymorphic dispatch is the right way
> > to handle it in OO languages, which implies that polymorphism can and
> > should take the place of any switch statement.
>
> That is a misreading.  I wrote, that the case logic is "probably"
> symptomatic of poor design. I advocated "considering" whether an OO
> solution might be more appropriate.  You misunderstand my intent.  I'm
> not here to postulate some ultimate OO truths, but to provide
> practical advice (aimed here mainly at Hong and Kee).  In regard elif
> chains, this is my advice.

Even granting that your post wasn't as drastic as it sounded (and
enough people reacted to your first post that you should should
"probably" "consider" whether it came off a little more strongly than
you intended), I think you are still overstating it by a lot.

OO polymorphic dispatching is good for some stuff, and simpler
dispatching such with if...else, a dict, or a hypotheical switch, is
good for some stuff. That's it. Having a bunch of if...elses is not
"probably" wrong, and it's not a red flag, unless you're not a good
programmer and have already missed a bunch of other more pertinent red
flags.


Carl Banks

Tim Chase

unread,
Dec 10, 2009, 9:38:31 AM12/10/09
to Asun Friere, pytho...@python.org
> This switch statement belongs to one guy. One guy who wants to know
> how to do everything that needs to be done to Phones no matter who
> asks

This is where you make a false assumption -- the contents and
parsing of the "switch" are provider-specific in this case,
mapping to a common ontology of the Phone object:

class MonopolyProvider1Parser:
...


switch row['recordtype']:
case '01':
phone.international += Decimal(row['internationalcost'])
// optionally a "break" here depending on
// C/C++/Java/PHP syntax vs. Pascal syntax which
// doesn't have fall-through
case '02':
phone.text_messaging += (
int(row['textmessages sent']) +
int(row['pages received']) +
int(row['textmessages sent']) +
int(row['pages received'])
...
default:
raise WhatTheHeckIsThis()

class MonopolyProvider2Parser:
...
switch row['recordtype']:
case 'abc':
phone.international += (
Decimal(row['canada cost']) +
Decimal(row['eu cost']) +
Decimal(row['mexico cost']) +
Decimal(row['other intl cost'])
)
case 'xyz':
phone.text_messaging += int(row['textmessages'])
...
default:
raise WhatTheHeckIsThis()

> # The one thing I'm sure I don't understand from the code is where the


> original rectypes comes into the process.

From the provider data -- sometimes CSV files, sometimes
tab-delimited text files, sometimes MS Access MDB files,
sometimes a web service...varies per-provider (and some providers
have multiple formats, like Verizon has MyBIZ and IBAS; ATT has
their WinCD and Premier; etc). No two formats are the same, so
the logic needed to parse the data into our internal homogenized
Phone data structure varies per each one. And the logic (or lack
thereof) used by many providers in creating their formats require
reverse-engineering most of them through trial-and-error, and
huge ugly if/elif/else chains.

> I wonder if you agree that it's bit cleaner now? It's an effective
> solution. I'm making no representation that it's the best.

It's clean if it were the solution to my problem -- however, the
mess comes from the profusion of provider formats.


> simple (and legible!) one liner. A provider "enhances" their format,
> or a new provider format is added, code in one class, not in every
> switch they might be involved in. But sorry, I don't mean to
> patronise, I'm sure you know the spiel.

Yes, having been programming since I was in middle-school (quick
calculation yields a "boy I'm old" estimate of about 20
years...does anybody miss 360k 5.25" floppy disks? :) and have
my degree in CS. So it's not my lack of programming
skill/knowledge, but rather your misunderstanding of the
problem-space. Not to patronize ;-)

-tkc


Ethan Furman

unread,
Dec 10, 2009, 12:52:41 PM12/10/09
to pytho...@python.org
Asun Friere wrote:
> On Dec 10, 2:00 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
>
>>On Dec 9, 5:02 pm, Asun Friere <afri...@yahoo.co.uk> wrote:
>>
>>
>>>On Dec 9, 7:08 pm, Carl Banks <pavlovevide...@gmail.com> wrote:
>>
>>>>What if the object is a string you just read from a file?
>>
>>>>How do you dispatch using polymorphism in that case?
>>
>>>This would be a pertinent question, were I advocating that _all_
>>>switch statements should, or even can, be replaced with "dispatch
>>>using polymorphism."
>>
>>Then why did you claim that a decent OO should never have a switch
>>statement then?
>>You argued that a decent language OO should never
>>have a switch statement because polymorphic dispatch is the right way
>>to handle it in OO languages, which implies that polymorphism can and
>>should take the place of any switch statement.
>
> That is a misreading. I wrote... [snip]

You wrote, and I quote, "For which reason python has no 'case'
statement and why *no decent OO language should* ." [emphasis added]

Just to be clear.

~Ethan~

Nobody

unread,
Dec 10, 2009, 1:16:44 PM12/10/09
to
On Thu, 10 Dec 2009 05:47:19 +0000, Steven D'Aprano wrote:

>>> I string together a bunch of elif statements to simulate a switch
>>>
>>> if foo == True:
>>> blah
>>> elif bar == True:
>>> blah blah
>>> elif bar == False:
>>> blarg
>>> elif ....
>>
>> This isn't what would normally be considered a switch (i.e. what C
>> considers a switch).
>
> Anyone would think that C was the only programming language in
> existence...

It's the only one I know of which calls such statements "switch"
statements. Most other languages call them "case" statements.

>> A switch tests the value of an expression against a
>> set of constants.
>
> In C. Things may be different in other languages.
>
> For example, I recall the so-called "4GL" (remember when that was the
> marketing term of choice for interpreted programming languages?)
> Hyperscript from Informix. I can't check the exact syntax right now, but
> it had a switch statement which allowed you to do either C-like tests
> against a single expression, or if-like multiple independent tests.

Interpreted languages generally don't care about the labels being
constant, so you can do e.g. (BBC BASIC V):

CASE TRUE OF
WHEN foo: blah
WHEN bar: blah blah
WHEN NOT(bar): blarg
ENDCASE

The test expression is compared against each case expression sequentially
until one matches; both the test expression and case expressions are
evaluated at run-time.

This is essentially just an if/elif chain with different syntax,
whereas a C-style switch may be signficiantly more efficient (e.g. using a
jump table or a balanced tree).

>> Compiled languages' switch statements typically require constant labels
>> as this enables various optimisations.
>
> Pascal, for example, can test against either single values, enumerated
> values, or a range of values:
>
> case n of
> 0:
> writeln('zero');
> 1, 2:
> writeln('one or two');
> 3...10:
> writeln('something between three and ten');
> else writeln('something different');
> end;

IOW, identical semantics to C, but with some extra syntax to avoid the
need to write multiple consecutive labels.

MRAB

unread,
Dec 10, 2009, 1:24:28 PM12/10/09
to pytho...@python.org
Originally the 'case' statement in Pascal didn't support ranges or a
default; they started as non-standard extensions in some
implementations. Originally, if none of the values matched then that
was a runtime error.

hong zhang

unread,
Dec 9, 2009, 2:32:18 PM12/9/09
to pytho...@python.org
List,

I got error says IndentationError in end of line.
I could not figure out why. See following:

$ ./cont-mcs
File "./cont-mcs", line 264
mcs1 = ht_val+cck_val+green_val+fat_val+sgi_val
^
IndentationError: unindent does not match any outer indentation level


Thanks for help.

--henry



Ben Finney

unread,
Dec 10, 2009, 7:58:57 PM12/10/09
to
hong zhang <henryz...@yahoo.com> writes:

> I got error says IndentationError in end of line.
> I could not figure out why.

Nor can we, without seeing the code to compare indentation levels.

> Thanks for help.

Please construct a minimal example (not a whole huge program), that we
can run to show the behaviour you're seeing.

--
\ “Any intelligent fool can make things bigger and more complex… |
`\ It takes a touch of genius – and a lot of courage – to move in |
_o__) the opposite direction.” —Albert Einstein |
Ben Finney

Terry Reedy

unread,
Dec 10, 2009, 7:58:43 PM12/10/09
to pytho...@python.org
hong zhang wrote:
> List,

>
> I got error says IndentationError in end of line.
> I could not figure out why. See following:
>
> $ ./cont-mcs
> File "./cont-mcs", line 264
> mcs1 = ht_val+cck_val+green_val+fat_val+sgi_val
> ^
> IndentationError: unindent does not match any outer indentation level

Look at previous lines. Make sure you only use tabs or only use spaces
and not both. If you still need help, post all code back to last
unindented line.


Gabriel Genellina

unread,
Dec 10, 2009, 8:36:10 PM12/10/09
to pytho...@python.org
En Wed, 09 Dec 2009 16:32:18 -0300, hong zhang <henryz...@yahoo.com>
escribi�:

> I got error says IndentationError in end of line.
> I could not figure out why. See following:
>
> $ ./cont-mcs
> File "./cont-mcs", line 264
> mcs1 = ht_val+cck_val+green_val+fat_val+sgi_val
> ^
> IndentationError: unindent does not match any outer indentation level

The caret ^ might be misplaced; look at start of line 264, or perhaps the
previous line.
Remember not to mix tabs and spaces.

--
Gabriel Genellina

Lie Ryan

unread,
Dec 10, 2009, 8:57:15 PM12/10/09
to

Look at the surrounding code. Indentation Errors often lies on the
previous few lines. Possibly caused by unmatching parentheses.

Bearophile

unread,
Dec 10, 2009, 9:51:04 PM12/10/09
to
Bruno Desthuilliers:

> Well, obviously such business rules must by no mean be hardcoded. You
> really need a "rule engine", configurable by your domain experts thru a
> DSL that we'll design specially for you. The rule engine will generate
> an AbstractScoreFactory that will instanciate appropriate IScore
> implementation objects that knows what to do.

[snip]

Thank you very much for bringing back some sanity in this newsgroup,
sometimes a good antiexample like yours is better than many
explanations.

Bye,
bearophile

John Bokma

unread,
Dec 10, 2009, 10:22:41 PM12/10/09
to
Tim Chase <pytho...@tim.thechases.com> writes:

Please don't delete attribution line(s), added:

Asun Friere writes:

>>
>> phone.update_from_record(record)
>>
>> This switch statement belongs to one guy. One guy who wants to know
>> how to do everything that needs to be done to Phones no matter who
>> asks
>
> This is where you make a false assumption -- the contents and parsing
> of the "switch" are provider-specific in this case, mapping to a
> common ontology of the Phone object:

In that case, why not give the classes Asun suggested all the same base
class: Phone?

>> I wonder if you agree that it's bit cleaner now? It's an effective
>> solution. I'm making no representation that it's the best.
>
> It's clean if it were the solution to my problem -- however, the mess
> comes from the profusion of provider formats.

Yup, and there is no other solution to that than to convert them to
something universal.

> Yes, having been programming since I was in middle-school (quick
> calculation yields a "boy I'm old" estimate of about 20 years...does
> anybody miss 360k 5.25" floppy disks? :)

I do miss cassette tapes and the wheeeeeee kkkrggrggrgrgrg of a program
loading.

--
John Bokma

Read my blog: http://johnbokma.com/
Hire me (Perl/Python): http://castleamber.com/

Peter Pearson

unread,
Dec 10, 2009, 10:59:36 PM12/10/09
to

Do you see what's wrong here? ...

Python 2.5.2 (r252:60911, Jul 22 2009, 15:35:03)
[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> if None:
... print x
... print y
File "<stdin>", line 3
print y


^
IndentationError: unindent does not match any outer indentation level
>>>

The problem is that "print y" is indented less than "print x",
meaning that the indented code block containing "print x" is over,
and yet "print y" is *not* indented the same as the "if".

--
To email me, substitute nowhere->spamcop, invalid->net.

Message has been deleted

Tim Chase

unread,
Dec 11, 2009, 1:21:49 PM12/11/09
to John Bokma, pytho...@python.org
On 12/10/2009 09:22 PM, John Bokma wrote:
> Tim Chase<pytho...@tim.thechases.com> writes:
>
> Please don't delete attribution line(s), added:
>
> Asun Friere writes:

I tend to prune them because a good newsreader will thread
messages and put my reply in the context of the message to which
I'm replying. Both Thunderbird and Claws Mail seem to have
correctly placed my message under Asun's message. However your
reply to mine failed to show up under my message in either
program (it showed up under the top-level post by Hong Zhang in
both apps). Looks like you're sending from Gnus...a
threading/reply bug in Gnus perhaps? Anyways...I'll leave them
in for this reply.

>>> phone.update_from_record(record)
>>>
>>> This switch statement belongs to one guy. One guy who wants to know
>>> how to do everything that needs to be done to Phones no matter who
>>> asks
>>
>> This is where you make a false assumption -- the contents and parsing
>> of the "switch" are provider-specific in this case, mapping to a
>> common ontology of the Phone object:
>
> In that case, why not give the classes Asun suggested all the same base
> class: Phone?

Whether the logic gets put in a subclass of Phone, or whether it
gets put in a provider Phone-factory (as it currently does...the
Provider superclass also knows how to auto-detect the format of a
filename/path passed to it and know whether it can handle it),
there's still a rat's-nest of if/else/switch type logic, each
branch of which usually involves a single assignment, or further
sub-branch checking. So I often have things that would
pseudo-code like

switch rectype:
case '01':
phone.textmessaging += row['txtmsgcost']
count = row['count']
switch subtype:
case 'a': phone.textmessagessent += count
case 'b': phone.textmessagesreceived += count
case 'c': phone.pagessent += count
case 'd': phone.pagesreceived += count
case '02':
...

which is fairly readable. However, with a method-dispatch
dictionary, this flattens the hierarchy to "def"s at the same
level (class definition scope), leaving the hierarchy visible
only in the call-graph.

Turning each of these a function would

- obfuscate the flow and processing hierarchy
- create a profusion of 1-3 line functions (about 50 Phone
attributes, times currently about 15 provider formats, plus a
duplication factor of about 10% where certain fields aggregate
from various sources in the data, accumulating in a single Phone
field)
- have the overhead of several million function-calls

>> Yes, having been programming since I was in middle-school (quick
>> calculation yields a "boy I'm old" estimate of about 20 years...does
>> anybody miss 360k 5.25" floppy disks? :)
>
> I do miss cassette tapes and the wheeeeeee kkkrggrggrgrgrg of a program
> loading.

Heh, one of the other things I miss: booting my Apple ][+ and
hitting the <Reset> button, resulting in a prompt where I could
code in under 2 seconds from power-on. Can't say I miss tape at
all though :)

-tkc

Asun Friere

unread,
Dec 11, 2009, 11:13:46 PM12/11/09
to
On Dec 11, 1:38 am, Tim Chase <python.l...@tim.thechases.com> wrote:

> It's clean if it were the solution to my problem

Picking out that line first, just to be clear about this.

You missed the disclaimer.

This was never meant to be a solution to your problem. It was
solution to the problem contained in the code you posted.

Carl, asked a question, in response you provided an example, and I
wrote a solution. I did so to in order to illustrate how something
resembling a dispatch mechansism can save a programmer from ending up
with a "rat's-nest" of elif chainage. I hope you did not
misunderstand me as advocating your pulling apart working production
code. Heck, I wouldn't, err ...don't, do this.

Which is _not_ to say that it isn't actually a solution your problem
as well! :)


>>[One guy analogy]


>
> This is where you make a false assumption

Again "I don't really know your problem only the code you've posted."
If I misunderstood the record types as originating from different
providers, I'm sure you will find that my code, which faithfully
reimplements yours, does not.

Fair enough. What you posted was a subset of your overall problem.
Obviously!

Inasmuch as the code that you posted accurately represents a subset of
your problem, the solution given is still applicable, at least to that
subset of your problem. It's just that each inidividual
MonopolyProvider will require its own set of data sources. In any
case, I'm sure you could work out it could be applied, if you put your
mind to it.

I agree this involves more code than the example required, possibly
for less gain and I believe it is still advantageous to organise the
code in a way which separates knowledge from logic. At least when
that knowledge is non-trivial.

> > # The one thing I'm sure I don't understand from the code is where the
> > original rectypes comes into the process.
>

Sorry, my worrying there about whether we need to look the appropriate
RecType up in that dict was just a distraction. The problem isn't in
the dict, but with that the try should only be trapping unknown types,
not problems of instantiation. I should have written:
try :
AppropriateRecType = rectypes[rectype]


except KeyError :
raise WhatTheHeckIsThisError('unknown rectype: %s' % rectype)

record = ApproriateRecType(row)

> From the provider data -- sometimes CSV files, sometimes
> tab-delimited text files, sometimes MS Access MDB files,
> sometimes a web service...
> varies per-provider (and some providers
> have multiple formats, like Verizon has MyBIZ and IBAS; ATT has
> their WinCD and Premier; etc). No two formats are the same, so
> the logic needed to parse the data into our internal homogenized
> Phone data structure varies per each one. And the logic (or lack
> thereof) used by many providers in creating their formats require
> reverse-engineering most of them through trial-and-error, and
> huge ugly if/elif/else chains.

I feel your pain, I really do ... and a dispatch mechanism could
relieve some of it. ;)

Now I'm not saying you should implement it on extant production code.
As I wrote above, I still use, and unfortunately maintain, older code
which is a hideous mess of elif chains. The task of challenging these
elif chains is daunting, and rewriting such working code won't rank in
my work priorities for the foreseeable future.

This is why, elif chains, when they first arise, or better perhaps,
when they first begin to grow, should be examined to see whether they
are a bud that ought to be nipped, whether they sensibly can be
nipped, and if so how. They are, in Carl's words, "red flags," at
least they are if your're "not a very good progammer," like me. I
invite all my fellow NVGPs to treat them as such as well. :)

I can compare extending and maintaining code I wrote using old school
elifs and flags to that written using the State and dispatch patterns,
(code which python makes practicable). OO makes life easier for an
older NVGP such as myself, a young gun like you might not need to rely
on clear code. ;)

Albert van der Horst

unread,
Dec 21, 2009, 9:53:38 AM12/21/09
to
In article <pan.2009.12...@REMOVE.THIS.cybersource.com.au>,

Steven D'Aprano <ste...@REMOVE.THIS.cybersource.com.au> wrote:

This shows where the merit of a switch/case statement lies.
It guarantees both to the compiler and to the human reader
that we are dealing with mutually exclusive and constant cases.
This allows optimisation (c) and verification (h) advantages.

A chain of elif's OTOH doesn't do that. On the top of my toes
I will have to check every line whether the regularity
breaks down.

As regards polymorphism. It is dubious that an inherently
different technique can be consistently and confidently
promoted as an alternative. Sometimes it is, sometimes it
isn't. Polymorphism is IMO promoted for far too much
situations. As a maintenance programmer (being at least
twice as smart as the people who wrote the original code)
I can testify that code can be polymorphed to the point
that it stops working and/or that it practically stops
working and/or that it stops working practically.

I'm the author of PostItFixup assembler. This assembler
manages to assemble code with no control structures
at all. 1]
No IF, no FOR, no WHILE, no CASES. Nothing conditional,
and no looping. How's that for a change?
Although this is truly remarkable and well worth studying,
I will not promote this style of programming as the ultimate
alternative for everything. (And it breaks down at a point,
sure.)

Bottom line: "Give to the Caesar what belongs to the Caesar..."

Not adding a switch to python is a matter of taste,
good taste as far as I'm concerned.

Adding a switch to C was also good taste, by the way.

>--
>Steven

Groetjes Albert

--

1] Only in the error checking there is an IF.
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Aahz

unread,
Dec 24, 2009, 11:06:21 AM12/24/09
to
In article <mailman.1736.1260555...@python.org>,

Tim Chase <pytho...@tim.thechases.com> wrote:
>On 12/10/2009 09:22 PM, John Bokma wrote:
>>
>> Please don't delete attribution line(s), added:
>>
>> Asun Friere writes:
>
>I tend to prune them because a good newsreader will thread messages
>and put my reply in the context of the message to which I'm replying.

You are assuming that everyone will get all messages. Even in this day
of better network connections, that is an ill-founded assumption.
Moreover, you have no assurance that the message will be read in the
context of a newsgroup; consider someone forwarding a single message.

Always always always provide attributions for quoting, it's just the
Right Thing.
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

Looking back over the years, after I learned Python I realized that I
never really had enjoyed programming before.

Reply all
Reply to author
Forward
0 new messages