Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 275 – Switching on Multiple Values

PEP 275 – Switching on Multiple Values

Author:
Marc-André Lemburg <mal at lemburg.com>
Status:
Rejected
Type:
Standards Track
Created:
10-Nov-2001
Python-Version:
2.6
Post-History:


Table of Contents

Rejection Notice

A similar PEP for Python 3000,PEP 3103, was already rejected,so this proposal has no chance of being accepted either.

Abstract

This PEP proposes strategies to enhance Python’s performancewith respect to handling switching on a single variable havingone of multiple possible values.

Problem

Up to Python 2.5, the typical way of writing multi-value switcheshas been to use long switch constructs of the following type:

ifx=='first state':...elifx=='second state':...elifx=='third state':...elifx=='fourth state':...else:# default handling...

This works fine for short switch constructs, since the overhead ofrepeated loading of a local (the variable x in this case) andcomparing it to some constant is low (it has a complexity of O(n)on average). However, when using such a construct to write a statemachine such as is needed for writing parsers the number ofpossible states can easily reach 10 or more cases.

The current solution to this problem lies in using a dispatchtable to find the case implementing method to execute depending onthe value of the switch variable (this can be tuned to have acomplexity of O(1) on average, e.g. by using perfect hashtables). This works well for state machines which require complexand lengthy processing in the different case methods. It does notperform well for ones which only process one or two instructionsper case, e.g.

defhandle_data(self,data):self.stack.append(data)

A nice example of this is the state machine implemented inpickle.py which is used to serialize Python objects. Otherprominent cases include XML SAX parsers and Internet protocolhandlers.

Proposed Solutions

This PEP proposes two different but not necessarily conflictingsolutions:

  1. Adding an optimization to the Python compiler and VMwhich detects the above if-elif-else construct andgenerates special opcodes for it which use a read-onlydictionary for storing jump offsets.
  2. Adding new syntax to Python which mimics the C styleswitch statement.

The first solution has the benefit of not relying on adding newkeywords to the language, while the second looks cleaner. Bothinvolve some run-time overhead to assure that the switchingvariable is immutable and hashable.

Both solutions use a dictionary lookup to find the rightjump location, so they both share the same problem space interms of requiring that both the switch variable and theconstants need to be compatible to the dictionary implementation(hashable, comparable, a==b => hash(a)==hash(b)).

Solution 1: Optimizing if-elif-else

Implementation:

It should be possible for the compiler to detect anif-elif-else construct which has the following signature:

ifx=='first':...elifx=='second':...else:...

i.e. the left hand side always references the same variable,the right hand side a hashable immutable builtin type. Theright hand sides need not be all of the same type, but theyshould be comparable to the type of the left hand switchvariable.

The compiler could then setup a read-only (perfect) hashtable, store it in the constants and add an opcode SWITCH infront of the standard if-elif-else byte code stream whichtriggers the following run-time behaviour:

At runtime, SWITCH would check x for being one of thewell-known immutable types (strings, unicode, numbers) anduse the hash table for finding the right opcode snippet. Ifthis condition is not met, the interpreter should revert tothe standard if-elif-else processing by simply skipping theSWITCH opcode and proceeding with the usual if-elif-else bytecode stream.

Issues:

The new optimization should not change the current Pythonsemantics (by reducing the number of__cmp__ calls and adding__hash__ calls in if-elif-else constructs which are affectedby the optimization). To assure this, switching can onlysafely be implemented either if a “from __future__” styleflag is used, or the switching variable is one of the builtinimmutable types: int, float, string, unicode, etc. (notsubtypes, since it’s not clear whether these are stillimmutable or not)

To prevent post-modifications of the jump-table dictionary(which could be used to reach protected code), the jump-tablewill have to be a read-only type (e.g. a read-onlydictionary).

The optimization should only be used for if-elif-elseconstructs which have a minimum number of n cases (where n isa number which has yet to be defined depending on performancetests).

Solution 2: Adding a switch statement to Python

New Syntax

switchEXPR:caseCONSTANT:SUITEcaseCONSTANT:SUITE...else:SUITE

(modulo indentation variations)

The “else” part is optional. If no else part is given andnone of the defined cases matches, no action is taken andthe switch statement is ignored. This is in line with thecurrent if-behaviour. A user who wants to signal thissituation using an exception can define an else-branchwhich then implements the intended action.

Note that the constants need not be all of the same type, butthey should be comparable to the type of the switch variable.

Implementation

The compiler would have to compile this into byte codesimilar to this:

defwhatis(x):switch(x):case'one':print'1'case'two':print'2'case'three':print'3'else:print"D'oh!"

into (omitting POP_TOP’s and SET_LINENO’s):

6LOAD_FAST0(x)9LOAD_CONST1(switch-table-1)12SWITCH26(to38)14LOAD_CONST2('1')17PRINT_ITEM18PRINT_NEWLINE19JUMP4322LOAD_CONST3('2')25PRINT_ITEM26PRINT_NEWLINE27JUMP4330LOAD_CONST4('3')33PRINT_ITEM34PRINT_NEWLINE35JUMP4338LOAD_CONST5("D'oh!")41PRINT_ITEM42PRINT_NEWLINE>>43LOAD_CONST0(None)46RETURN_VALUE

Where the ‘SWITCH’ opcode would jump to 14, 22, 30 or 38depending on ‘x’.

Thomas Wouters has written a patch which demonstrates theabove. You can download it from[1].

Issues

The switch statement should not implement fall-throughbehaviour (as does the switch statement in C). Each casedefines a complete and independent suite; much like in anif-elif-else statement. This also enables using break inswitch statements inside loops.

If the interpreter finds that the switch variable x isnot hashable, it should raise a TypeError at run-timepointing out the problem.

There have been other proposals for the syntax which reuseexisting keywords and avoid adding two new ones (“switch” and“case”). Others have argued that the keywords should use newterms to avoid confusion with the C keywords of the same namebut slightly different semantics (e.g. fall-through withoutbreak). Some of the proposed variants:

caseEXPR:ofCONSTANT:SUITEofCONSTANT:SUITEelse:SUITEcaseEXPR:ifCONSTANT:SUITEifCONSTANT:SUITEelse:SUITEwhenEXPR:inCONSTANT_TUPLE:SUITEinCONSTANT_TUPLE:SUITE...else:SUITE

The switch statement could be extended to allow multiplevalues for one section (e.g. case ‘a’, ‘b’, ‘c’: …). Anotherproposed extension would allow ranges of values (e.g. case10..14: …). These should probably be post-poned, but alreadykept in mind when designing and implementing a first version.

Examples

The following examples all use a new syntax as proposed bysolution 2. However, all of these examples would work withsolution 1 as well.

switchEXPR:switchx:caseCONSTANT:case"first":SUITEprintxcaseCONSTANT:case"second":SUITEx=x**2...printxelse:else:SUITEprint"whoops!"caseEXPR:casex:ofCONSTANT:of"first":SUITEprintxofCONSTANT:of"second":SUITEprintx**2else:else:SUITEprint"whoops!"caseEXPR:casestate:ifCONSTANT:if"first":SUITEstate="second"ifCONSTANT:if"second":SUITEstate="third"else:else:SUITEstate="first"whenEXPR:whenstate:inCONSTANT_TUPLE:in("first","second"):SUITEprintstateinCONSTANT_TUPLE:state=next_state(state)SUITEin("seventh",):...print"done"else:break# out of loop!SUITEelse:print"middle state"state=next_state(state)

Here’s another nice application found by Jack Jansen (switchingon argument types):

switchtype(x).__name__:case'int':SUITEcase'string':SUITE

Scope

XXX Explain “from __future__ import switch”

Credits

  • Martin von Löwis (issues with the optimization idea)
  • Thomas Wouters (switch statement + byte code compiler example)
  • Skip Montanaro (dispatching ideas, examples)
  • Donald Beaudry (switch syntax)
  • Greg Ewing (switch syntax)
  • Jack Jansen (type switching examples)

References

[1]
https://sourceforge.net/tracker/index.php?func=detail&aid=481118&group_id=5470&atid=305470

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0275.rst

Last modified:2025-02-01 08:55:40 GMT


[8]ページ先頭

©2009-2026 Movatter.jp