Is there no switch function in Python

Bengt Richter bokr at oz.net
Fri Sep 10 23:29:48 CEST 2004


On Thu, 9 Sep 2004 22:27:02 +0200, aleaxit at yahoo.com (Alex Martelli) wrote:

>Rudi Hansen <rsh_remove_this_ at pobox.dk> wrote:
>
>> I dont seem to be able to find the switch statement in Python.
>
>Right, there isn't one.
>
>> 
>> I would like to be able to do
>> 
>> switch(var)
>>     case 1 :
>>         print "var = 1"
>>     case 2:
>>         print "var = 2"
>> 
>> But it seems that i have to do.
>> 
>> if(var=1)
>>     print "var =1"
>> elseif(var=2)
>>     print "var=2"
>> 
>> Is ther no easier way?? 
>
>several, starting with
>
>if var in (1,2): print 'var = %s' % var
>
>The most Pythonic idiom, when you have to do something substantial in
>each branch of the switch, is a dictionary of callables -- in this toy
>case it might be:
>
>switch = {1: lambda: sys.stdout.write('var=1\n'),
>               2: lambda: sys.stdout.write('var=2\n'), }
>switch.get(var, lambda: '')()
>
>An if/elif tree is also fine, though the syntax is not as you think...:
>
>if     var == 1:
>    print 'var is one'
>elif  var == 2:
>    print 'var is two'
>
The trouble is that other than if/elif/else or some weird exception tricks,
you can't switch between statements that execute as if they were suites of if/elif/else -- i.e.,
in the local namespace. And UIAM current exec introduces special restrictions in a function.
So you can't get a constant-time "jump table" effect.

But IWT if we had named local code suites, we could exec them safely via a mapping. E.g.,

    def foo(var): # following is not legal code ;-)
        v1: print 'var is one'
        v2: # any suite should be ok, not just one-liners
            print 'var',
            print 'is two'
        switch = {1:v1, 2:v2}    
        exec switch.get(var, '')

It would also be nice if the local bindings of named local code suites were
(or became wrapped on access as) objects that were callable like functions
without parameters. Locally that could be optimized, but if e.g. foo returned
v2 to its caller, there would have to be a wrapper with a mutable closure
capturing the current name space IWT. This would also be interesting to
pass to another function, which then could effectively execute a code suite
in _its_ caller's space. This could lead to all kinds of wild things, _some_ of
which would probably be neat and useful ;-)

It would be nice to get the switch mapping built at def-time, which should be
feasible, since the v1,v2 etc, bindings could be available then. Not sure how
to spell it nicely though, except that I still like the notion of a general
way to specify def-time execution of statements or expression terms. E.g.,
using '..' as a syntactic marker,
         switch ..= {1:v1, 2:v2}
could mean create the binding at def-time (necessarily evaluating the rhs then also).

I had previously proposed that
    def foo():
        x ..= 123
        return x
be codewise equivalent to current
    def foo(x=123):
        return x
without having x appear in the argument list, but still having the same
call-time local-binding initialization effect (i.e., new binding to def-time rhs value).
I mention this because v1 and v2 in the example are not in the namespace that is normally
searched for default arg rhs expressions as in def foo(x=name): ..., so named local suites'
names would have to be included (and allowed to shadow names that might otherwise be found).

No one commented on my previous post on that, perhaps because I didn't split it out into
a separate post with appropriate title. So here I did it again ;-/ The main use would be
efficient local binding initialization without using the default-arg hack or using a factory
funtion to return a function with initialized bindings in a closure.

The other use would allow constant folding for terms in expressions, using ..(expr) as
as spelling for evaluating expr at def-time and using a constant binding to the result
at run-time. E.g.,

    def circle_area(r): return ..(__import__('math').pi)*r*r

would generate byte code like what you can now do with Raymond Hettinger's global-constant
optimizing decorator by writing:

    pi = __import__('math').pi # just to make it look like the expr above
    @bind_constants()
    def circle_area(r): return pi*r*r
    del pi

(Very nice, Raymond)

Maybe Raymond will make a decorator to do local variable initialization, so we can write
    @localinit('name', expr)
    def foo():
        name = name*2 # usable like default name in def foo(name=expr): ...
        return name
;-)

Regards,
Bengt Richter



More information about the Python-list mailing list