Skip to content Skip to sidebar Skip to footer

Switch-case In Python Doesn't Work; Need Another Pattern

I need a help with some code here. I wanted to implement the switch case pattern in Python, so like some tutorial said, I can use a dictionary for that, but here is my problem: #

Solution 1:

message = { 'create':msg(some_data or''),
            'update':msg(other_data or'')
            # can have more
          }

Better yet, to prevent msg from being executed just to fill the dict:

message = { 'create':(msg,some_data),
            'update':(msg,other_data),
            # can have more
          }
func,data=message[msg_type]func(data)

and now you are free to define a more sensible msg function which can deal with an argument equal to None:

def msg(data):
    ifdatais None: data=''
    ...

Solution 2:

It sounds like you're complicating this more than you need to. If you want it simple, use:

if mytype == 'create':
    return msg(some_data)
elif mytype == 'update':
    return msg(other_data)
else:
    return msg(default_data)

You don't have to use dicts and function references just because you can. Sometimes a boring, explicit if/else block is exactly what you need. It's clear to even the newest programmers on your team and won't call msg() unnecessarily, ever. I'm also willing to bet that this will be faster than the other solution you were working on unless the number of cases grows large and msg() is lightning fast.

Solution 3:

Turns out the jokes on me and I was pwned in the switch inventing game five years before I even learned Python: Readable switch construction without lambdas or dictionaries. Oh well. Read below for another way to do it.


Here. Have a switch statement (with some nice cleanups by @martineau):

with switch(foo):

    @case(1)def_():
        print"1"    @case(2)def_():
        print"2"    @case(3)def_():
        print"3"    @case(5)    @case(6)def_():
        print'5 and 6'    @case.defaultdef_():
        print'default'

I'll toss in the (moderately) hacked stack, abused decorators and questionable context manager for free. It's ugly, but functional (and not in the good way). Essentially, all it does is wrap the dictionary logic up in an ugly wrapper.

import inspect

classswitch(object):
    def__init__(self, var):
        self.cases = {}
        self.var = var


    def__enter__(self):
        defcase(value):
            defdecorator(f):
                if value notin self.cases:
                    self.cases[value] = f
                return f
            return decorator

        defdefault(f):
            self.default = f
            return f
        case.default = default

        f_locals = inspect.currentframe().f_back.f_locals
        self.f_locals = f_locals.copy()
        f_locals['case'] = case

    def__exit__(self, *args, **kwargs):
        new_locals = inspect.currentframe().f_back.f_locals
        new_items = [key for key in new_locals if key notin self.f_locals]
        for key in new_items:
             del new_locals[key]              # clean up
        new_locals.update(self.f_locals)      # this reverts all variables to theirtry:                                  # previous values
            self.cases[self.var]()
        except KeyError:
            try:
                getattr(self, 'default')()
            except AttributeError:
                pass

Note that the hacked stack isn't actually necessary. We just use it to create a scope so that definitions that occur in the switch statement don't leak out into the enclosing scope and to get case into the namespace. If you don't mind leaks (loops leak for instance), then you can remove the stack hack and return the case decorator from __enter__, using the as clause on the with statement to receive it in the enclosing scope.

Solution 4:

Here's something a little different (although somewhat similar to @Tumbleweed's) and arguably more "object-oriented". Instead of explicitly using a dictionary to handle the various cases, you could use a Python class (which contains a dictionary).

This approach provides a fairly natural looking translation of C/C++ switch statements into Python code. Like the latter it defers execution of the code that handles each case and allows a default one to be provided.

The code for each switch class method that corresponds to a case can consist of more than one line of code instead of the single return <expression> ones shown here and are all compiled only once. One difference or limitation though, is that the handling in one method won't and can't be made to automatically "fall-though" into the code of the following one (which isn't an issue in the example, but would be nice).

classswitch:defcreate(self): return"blabla %s" % msg(some_data)
    defupdate(self): return"blabla %s" % msg(other_data)
    defdelete(self): return"blabla %s" % diff(other_data, some_data)
    def_default(self): return"unknown type_"def__call__(self, type_): return getattr(self, type_, self._default)()

switch = switch() # only needed oncereturn switch(type_)

Solution 5:

You could hide the evaluation inside a lambda:

message = { 'create': lambda: msg(some_data),
            'update': lambda: msg(other_data),
          }
return message[type]()

As long as the names are all defined (so you don’t get a NameError), you could also structure it like this:

message = { 'create': (msg, some_data),
            'update': (other_func, other_data),
          }
func, arg = message[type]returnfunc(arg)

Post a Comment for "Switch-case In Python Doesn't Work; Need Another Pattern"