HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Switch function in Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
switchfunctionpython

Problem

I wrote this switch function for Python, as there isn't a built-in one and most of the Googlable ones doesn't execute code, but only return something (I admit I haven't looked much further than a single Stack Oveflow page to see what people use, but a lot of the solutions are unnecessarily complex):

def switch(variable, **cases):
    for case in cases.keys():
       if variable is case:
           exec(cases[case])


I use **cases, due to this syntax:

def switch(variable, **cases):
    for case in cases.keys():
       if variable is case:
           exec(cases[case])

some_string = "a"
switch(some_string,
       b="print('It is b!')",
       a="print('It is a!')")

some_string = "b"
switch(some_string,
       b="print('It is b!')",
       a="print('It is a!')")

some_string = "c"
switch(some_string,
       b="print('It is b!')",
       a="print('It is a!')")


Can this be optimized/made more pythonic?

Solution

The most Pythonic way to do is to define a (reusable) dictionary. Most often this dictionary contains callables, but not necessarily.

The least efficient way to do it, though, is to loop over each possibility as you do. It computes in \$O(n)\$ rather than \$O(1)\$ as a dictionnary can offer. And avoid the capability of short-circuit a succession of if...elif...elif...else can offer.

So, at the very least, use something like

def switch(value, **cases):
    try:
        statement = cases[value]
    except KeyError:
        pass
    else:
        exec(statement)


However, I find that defining the various statements at each call is innefficient. As I said, it's better to be able to define a reusable dictionary. I would also advise against the use of exec and make the function more generic; letting the user decide what to do with the retrived value:

def switch(value, cases, default=None):
    try:
        return cases[value]
    except KeyError:
        return default


Usage being:

PRINT_STUFF = {
    'a': lambda: print("It's a!"),
    'b': lambda: print("It's b!"),
}

switch('a', PRINT_STUFF, lambda: None)()
switch('b', PRINT_STUFF, lambda: None)()
switch('c', PRINT_STUFF, lambda: None)()


But way... This try..except reminds me of something... Oh the dict.get method. So there is really no need for switch:

PRINT_STUFF.get('a', lambda: None)()
PRINT_STUFF.get('b', lambda: None)()
PRINT_STUFF.get('c', lambda: None)()


Or, if you trully insist on using strings rather than real callable objects:

PRINT_STUFF = {
    'a': 'print("It\'s a!")',
    'b': 'print("It\'s b!")',
}

exec(PRINT_STUFF.get('a', 'pass'))
exec(PRINT_STUFF.get('b', 'pass'))
exec(PRINT_STUFF.get('c', 'pass'))

Code Snippets

def switch(value, **cases):
    try:
        statement = cases[value]
    except KeyError:
        pass
    else:
        exec(statement)
def switch(value, cases, default=None):
    try:
        return cases[value]
    except KeyError:
        return default
PRINT_STUFF = {
    'a': lambda: print("It's a!"),
    'b': lambda: print("It's b!"),
}

switch('a', PRINT_STUFF, lambda: None)()
switch('b', PRINT_STUFF, lambda: None)()
switch('c', PRINT_STUFF, lambda: None)()
PRINT_STUFF.get('a', lambda: None)()
PRINT_STUFF.get('b', lambda: None)()
PRINT_STUFF.get('c', lambda: None)()
PRINT_STUFF = {
    'a': 'print("It\'s a!")',
    'b': 'print("It\'s b!")',
}

exec(PRINT_STUFF.get('a', 'pass'))
exec(PRINT_STUFF.get('b', 'pass'))
exec(PRINT_STUFF.get('c', 'pass'))

Context

StackExchange Code Review Q#149922, answer score: 6

Revisions (0)

No revisions yet.