Add PEP 3103: A Switch/Case Statement (GvR).
Also fix a reference to cvs in the Makefile.
This commit is contained in:
parent
493d7e9d7d
commit
3e857976b3
2
Makefile
2
Makefile
|
@ -23,4 +23,4 @@ clean:
|
||||||
-rm *.html
|
-rm *.html
|
||||||
|
|
||||||
update:
|
update:
|
||||||
cvs update -P -d
|
svn update
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
126
pep-3103.txt
126
pep-3103.txt
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue