[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:
> >>[Phillip]
> >>1. "case (literal|NAME)" is the syntax for equality testing -- you can't
> >>use an arbitrary expression, not even a dotted name.
> >[Guido]
> >But dotted names are important! E.g. case re.DOTALL. And sometimes
> >compile-time constant expressions are too. Example: case sys.maxint-1.
> [Phillip]
> 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
module.

> >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 mailing list