Add PEP 3103: A Switch/Case Statement (GvR).

Also fix a reference to cvs in the Makefile.
This commit is contained in:
Guido van Rossum 2006-06-26 18:05:39 +00:00
parent 493d7e9d7d
commit 3e857976b3
3 changed files with 111 additions and 19 deletions

View File

@ -23,4 +23,4 @@ clean:
-rm *.html -rm *.html
update: update:
cvs update -P -d svn update

View File

@ -99,6 +99,7 @@ Index by Category
S 754 IEEE 754 Floating Point Special Values Warnes S 754 IEEE 754 Floating Point Special Values Warnes
S 3101 Advanced String Formatting Talin S 3101 Advanced String Formatting Talin
S 3102 Keyword-Only Arguments Talin S 3102 Keyword-Only Arguments Talin
S 3103 A Switch/Case Statement GvR
Finished PEPs (done, implemented in Subversion) Finished PEPs (done, implemented in Subversion)
@ -431,6 +432,7 @@ Numerical Index
I 3100 Python 3.0 Plans Kuchling, Cannon I 3100 Python 3.0 Plans Kuchling, Cannon
S 3101 Advanced String Formatting Talin S 3101 Advanced String Formatting Talin
S 3102 Keyword-Only Arguments Talin S 3102 Keyword-Only Arguments Talin
S 3103 A Switch/Case Statement GvR
Key Key

View File

@ -8,7 +8,7 @@ Type: Standards Track
Python-Version: 3.0 Python-Version: 3.0
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 25-Jun-2006 Created: 25-Jun-2006
Post-History: never Post-History: 26-Jun-2006
Abstract Abstract
@ -24,6 +24,11 @@ This PEP should be seen as an alternative to PEP 275. My views are
somewhat different from that PEP's author, but I'm grateful for the somewhat different from that PEP's author, but I'm grateful for the
work done in that PEP. work done in that PEP.
This PEP introduces canonical names for the many variants that have
been discussed for different aspects of the syntax and semantics, such
as "alternative 2", "school II", "Option 3" and so on. Hopefully
these names will help the discussion.
Rationale Rationale
========= =========
@ -216,10 +221,10 @@ like this::
case [*]EXPR, [*]EXPR, ...: case [*]EXPR, [*]EXPR, ...:
Note that the * notation is similar to the use of prefix * already in The * notation is similar to the use of prefix * already in use for
use for variable-length parameter lists and for passing computed variable-length parameter lists and for passing computed argument
argument lists, and often proposed for value-unpacking (e.g. "a, b, lists, and often proposed for value-unpacking (e.g. "a, b, *c = X" as
*c = X" as an alternative to "(a, b), c = X[:2], X[2:]"). an alternative to "(a, b), c = X[:2], X[2:]").
Alternative D Alternative D
------------- -------------
@ -390,16 +395,17 @@ otherwise an if/elif chain would have little chance of success either.
Assuming the switch will be executed many times, doing some extra work Assuming the switch will be executed many times, doing some extra work
the first time pays back quickly by very quick dispatch times later. the first time pays back quickly by very quick dispatch times later.
A mostly theoretical objection to this option is that there is no An objection to this option is that there is no obvious object where
obvious object where the dispatch dict can be stored. It can't be the dispatch dict can be stored. It can't be stored on the code
stored on the code object, which is supposed to be immutable; it can't object, which is supposed to be immutable; it can't be stored on the
be stored on the function object, since many function objects may be function object, since many function objects may be created for the
created for the same function (e.g. for nested functions). In same function (e.g. for nested functions). In practice, I'm sure that
practice, I'm sure that something can be found; it could be stored in something can be found; it could be stored in a section of the code
a section of the code object that's not considered when comparing two object that's not considered when comparing two code objects or when
code objects or when pickling or marshalling a code object; or all pickling or marshalling a code object; or all switches could be stored
switches could be stored in a dict indexed by weak references to code in a dict indexed by weak references to code objects. The solution
objects. should also be careful not to leak switch dicts between multiple
interpreters.
Another objection is that the first-use rule allows obfuscated code Another objection is that the first-use rule allows obfuscated code
like this:: like this::
@ -419,17 +425,101 @@ equivalent to this::
but that's not what it does (unless it is always called with the same but that's not what it does (unless it is always called with the same
value as the second argument). This has been addressed by suggesting value as the second argument). This has been addressed by suggesting
that the case expressions should not be allowed to reference local that the case expressions should not be allowed to reference local
variables. But this is somewhat arbitrary. variables, but this is somewhat arbitrary.
A final objection is that in a multi-threaded application, the A final objection is that in a multi-threaded application, the
first-use rule requires intricate locking in order to guarantee the first-use rule requires intricate locking in order to guarantee the
correct semantics. (The first-use rule suggests a promise that side correct semantics. (The first-use rule suggests a promise that side
effects of case expressions are incurred exactly once.) effects of case expressions are incurred exactly once.) This may be
as tricky as the import lock has proved to be, since the lock has to
be held while all the case expressions are being evaluated.
Option 3 Option 3
'''''''' ''''''''
TBD A proposal that has been winning support (including mine) is to freeze
a switch's dict when the innermost function containing it is defined.
The switch dict is stored on the function object, just as parameter
defaults are, and in fact the case expressions are evaluated at the
same time and in the same scope as the parameter defaults (i.e. in the
scope containing the function definition).
This option has the advantage of avoiding many of the finesses needed
to make option 2 work: there's no need for locking, no worry about
immutable code objects or multiple interpreters. It also provides a
clear explanation for why locals can't be referenced in case
expressions.
This option works just as well for situations where one would
typically use a switch; case expressions involving imported or global
named constants work exactly the same way as in option 2, as long as
they are imported or defined before the function definition is
encountered.
A downside however is that the dispatch dict for a switch inside a
nested function must be recomputed each time the nested function is
defined. For certain "functional" styles of programming this may make
switch unattractive in nested functions. (Unless all case expressions
are compile-time constants; then the compiler is of course free to
optimize away the swich freezing code and make the dispatch table part
of the code object.)
Another downside is that under this option, there's no clear moment
when the dispatch dict is frozen for a switch that doesn't occur
inside a function. There are a few pragmatic choices for how to treat
a switch outside a function:
(a) Disallow it.
(b) Translate it into an if/elif chain.
(c) Allow only compile-time constant expressions.
(d) Compute the dispatch dict each time the switch is reached.
(e) Like (b) but tests that all expressions evaluated are hashable.
Of these, (a) seems too restrictive: it's uniformly worse than (c);
and (d) has poor performance for little or no benefits compared to
(b). It doesn't make sense to have a performance-critical inner loop
at the module level, as all local variable references are slow there;
hence (b) is my (weak) favorite. Perhaps I should favor (e), which
attempts to prevent atypical use of a switch; examples that work
interactively but not in a function are annoying. In the end I don't
think this issue is all that important (except it must be resolved
somehow) and am willing to leave it up to whoever ends up implementing
it.
When a switch occurs in a class but not in a function, we can freeze
the dispatch dict at the same time the temporary function object
representing the class body is created. This means the case
expressions can reference module globals but not class variables.
Alternatively, if we choose (b) above, we could choose this
implementation inside a class definition as well.
Option 4
''''''''
There are a number of proposals to add a construct to the language
that makes the concept of a value pre-computed at function definition
time generally available, without tying it either to parameter default
values or case expressions. Some keywords proposed include 'const',
'static', 'only' or 'cached'. The associated syntax and semantics
vary.
These proposals are out of scope for this PEP, except to suggest that
*if* such a proposal is accepted, there are two ways for the switch to
benefit: we could require case expressions to be either compile-time
constants or pre-computed values; or we could make pre-computed values
the default (and only) evaluation mode for case expressions. The
latter would be my preference, since I don't see a use for more
dynamic case expressions that isn't addressed adequately by writing an
explicit if/elif chain.
Conclusion
==========
It is too early to decide. I'd like to see at least one completed
proposal for pre-computed values before deciding. In the mean time,
Python is fine without a switch statement, and perhaps those who claim
it would be a mistake to add one are right.
Copyright Copyright