[Python-Dev] Switch statement
Guido van Rossum
guido at python.org
Thu Jun 22 21:54:29 CEST 2006
On 6/22/06, Phillip J. Eby <pje at telecommunity.com> wrote:
> >>1. "case (literal|NAME)" is the syntax for equality testing -- you can't
> >>use an arbitrary expression, not even a dotted name.
> >But dotted names are important! E.g. case re.DOTALL. And sometimes
> >compile-time constant expressions are too. Example: case sys.maxint-1.
> True - but at least you *can* use them, with "from re import DOTALL" and
> "maxint_less_1 = sys.maxint-1". You're just required to disambiguate
> *when* the calculation of these values is to be performed.
Yeah, but the result is a quite crippled case expression that's not
like anything in Python.
> >>2. NAME, if used, must be bound at most once in its defining scope
> >That's fine -- but doesn't extend to dotted names.
> Right, hence #1.
Which I don't like.
(I know, I'm repeating myself here. Better than contradicting myself. :-)
> >>3. Dictionary optimization can occur only for literals and names not bound
> >>in the local scope, others must use if-then.
> >So this wouldn't be optimized?!
> >NL = "\n"
> >for line in sys.stdin:
> > switch line:
> > "abc\n": ...
> > NL: ...
> This would result in a switch dictionary with "abc\n" in it, preceded by an
> if line==NL test. So it's half-optimized. The more literals, the more
> optimized. If you put the same switch in a function body, it becomes fully
> optimized if the NL binding stays outside the function definition.
That still seems really weird, especially if you consider the whole
thing already being inside a def(). It would optimize references to
non-locals but not references to locals...?
> Note that you previously proposed a switch at top level not be optimized at
> all, so this is an improvement over that.
I don't particularly care about top-level switches; I don't expect
they'll be used much and I don't expect people to care about their
speed much. A for loop using some local variables is also quite slow
outside a function; if anybody complains we just tell them to put it
in a function.
I do care about switch/case being easy to use and flexible in likely
use cases, which include using constants defined in a different
> >I like it better than const declarations, but I don't like it as much
> >as the def-time-switch-freezing proposal; I find the limitiation to
> >simple literals and names too restrictive, and there isn't anything
> >else like that in Python.
> Well, you can't "def" a dotted name, but I realize this isn't a binding.
You could have left that out of your email and we'd all have been happier. :-)
> >I also don't like the possibility that it
> >degenerates to if/elif. I like predictability.
> It is predictable: anything defined in the same scope will be if/elif,
> anything defined outside will be dict-switched.
But that's pretty subtle. I'd much rather see a rule that
*effectively* rules out non-constant cases completely. IMO the
def-time-switch-freeze proposal does this.
> >I like to be able to switch on dotted names.
> >Also, when using a set in a case, one should be able to use an
> >expression like s1|s2 in a case.
> ...which then gets us back to the question of when the dots or "|" are
> evaluated. My proposal forces you to make the evaluation time explicit,
> visible, and unquestionably obvious in the source, rather than relying on
> invisible knowledge about the function definition time.
At the cost of more convoluted code. It means in many cases I'd
probably continue to use if/elif chains because refactoring it into a
switch is too much effort. Thereby relegating switch to something only
used by speed freaks. While I want my switch to be fast, I don't want
it to be a freak.
> "First time use" is also a more visible approach, because it does not
> contradict the user's assumption that evaluation takes place where the
> expression appears. The "invisible" assumption is only that subsequent
> execution will reuse the same expression results without recalculating them
> -- it doesn't *move* the evaluation somewhere else.
Have you made up your mind yet where the result of the first-time
evaluated value should be stored? On the function object? That implies
that it doesn't help for inner defs that are called only once per
definition (like certain callback patterns).
> I seem to recall that in general, Python prefers to evaluate expressions in
> the order that they appear in source code, and that we try to preserve that
> property as much as possible. Both the "names and literals only" and
> "first-time use" approaches preserve that property; "function definition
> time" does not.
But first-time has the very big disadvantage IMO that there's no
safeguard to warn you that the value is different on a subsequent
execution -- you just get the old value without warning.
> Of course, it's up to you to weigh the cost and benefit; I just wanted to
> bring this one specific factor (transparency of the source) to your
> attention. This whole "const" thread was just me trying to find another
> approach besides "first-time use" that preserves that visibility property
> for readers of the code.
Summarizing our disagreement, I think you feel that
freeze-on-first-use is most easily explained and understood while I
feel that freeze-at-def-time is more robust. I'm not sure how to get
past this point except by stating that you haven't convinced me... I
think it's time to sit back and wait for someone else to weigh in with
a new argument.
--Guido van Rossum (home page: http://www.python.org/~guido/)
More information about the Python-Dev