By Michael Schneider


2008-09-13 00:36:30 8 Comments

I want to write a function in Python that returns different fixed values based on the value of an input index.

In other languages I would use a switch or case statement, but Python does not appear to have a switch statement. What are the recommended Python solutions in this scenario?

30 comments

@Matthew Schinckel 2008-09-13 01:10:58

In addition to the dictionary methods (which I really like, BTW), you can also use if-elif-else to obtain the switch/case/default functionality:

if x == 'a':
    # Do the thing
elif x == 'b':
    # Do the other thing
if x in 'bc':
    # Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
    # Do yet another thing
else:
    # Do the default

This of course is not identical to switch/case - you cannot have fall-through as easily as leaving off the break statement, but you can have a more complicated test. Its formatting is nicer than a series of nested ifs, even though functionally that's what it is closer to.

@martyglaubitz 2013-05-18 10:30:13

i'd really prefer this, it uses a standart language construct and doesn't throw a KeyError if no matching case is found

@Martin Thoma 2015-06-25 06:33:35

I thought about the dictionary / get way, but the standard way is simply more readable.

@Arijoon 2015-08-26 20:37:44

Doesn't exactly operate like a switch statement but very close. In my opinion closest thing

@bmacnaughton 2015-12-30 16:32:53

This is the cleanest solution. It's most common that each switch has a break and the most common use for fall-through that I see is to match multiple elements, as in if x in 'bc':.

@some user 2016-02-29 18:58:48

But the variable 'x' is repeated multiple times. If you need to replace x, it is a lot easier with a switch/case statement. Also, if/elif/else gives you too much freedom (as seen in example above, mixing == and in, conditions could overlap and that becomes hard to read.

@Matthew Schinckel 2016-03-03 06:55:38

@someuser but the fact they can "overlap" is a feature. You just make sure the order is the priority in which matches should occur. As for repeated x: just do an x = the.other.thing before. Typically, you'd have a single if, multiple elif and a single else, as that's easier to understand.

@Matthew Schinckel 2016-03-03 06:57:23

Also, it needn't have the same type of test applied to x each time. i.e., == vs in vs not in or whatever.

@some user 2016-03-03 21:33:12

@Matthew: Simply put, if you need that kind of flexibility, you use if/elif/else. But most who visit this topic don't. We want more structural code. Any language that supports switch already supports if/elif/else.

@Alois Mahdal 2016-05-30 11:40:41

Nice, the "Fall-through by not using elif " is a bit confusing, though. What about this: forget about "fall through" and just accept it as two if/elif/else's?

@Lohmar ASHAR 2018-08-28 11:46:21

Also worth mentioning, when using things like x in 'bc', keep in mind that "" in "bc" is True.

@Armstrongest 2019-04-01 21:33:05

This really is the answer. Sure you can create a dictionary construct, but the if/elif is the most clear.

@Alejandro Quintanar 2018-02-17 21:54:47

Solution to run functions:

result = {
    'case1':     foo1, 
    'case2':     foo2,
    'case3':     foo3,
    'default':   default,
}.get(option)()

where foo1(), foo2(), foo3() and default() are functions

@Alejandro Quintanar 2018-02-20 21:27:44

Yes, for example if your variable option=="case2" your result=foo2()

@Alejandro Quintanar 2018-02-20 21:41:30

and so and so forth.

@Brian Underwood 2018-02-21 17:57:12

Yes, I understand the purpose. But my concern is that if you only want foo2(), the foo1(), foo3(), and default() functions are all also going to run, meaning things could take a long time

@Alejandro Quintanar 2018-02-22 06:10:37

You are completely right, this simple version runs all the functions at the directory creation, you can solve it passing 'option' as function parameter in foo(), here is a working example: gist.github.com/anonymous/a5ed3953e28440b4368c338b1e19d294

@timgeb 2018-04-10 06:00:08

omit the () inside the dictionary. use get(option)(). problem solved.

@Alejandro Quintanar 2018-04-17 05:13:40

Excellent the use of () is a grate solution, I made a gist to test it gist.github.com/aquintanar/01e9920d8341c5c6252d507669758fe5

@Solomon Ucko 2018-02-05 01:54:06

Simple, not tested; each condition is evaluated independently: there is no fall-through, but all cases are evaluated (although the expression to switch on is only evaluated once), unless there is a break statement. For example,

for case in [expression]:
    if case == 1:
        print(end='Was 1. ')

    if case == 2:
        print(end='Was 2. ')
        break

    if case in (1, 2):
        print(end='Was 1 or 2. ')

    print(end='Was something. ')

prints Was 1. Was 1 or 2. Was something. (Dammit! Why can't I have trailing whitespace in inline code blocks?) if expression evaluates to 1, Was 2. if expression evaluates to 2, or Was something. if expression evaluates to something else.

@syockit 2019-01-28 11:17:38

Well, the fall through works, but only to go to do_default.

@Ian Bell 2015-05-03 09:05:43

class Switch:
    def __init__(self, value):
        self.value = value

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        return False # Allows a traceback to occur

    def __call__(self, *values):
        return self.value in values


from datetime import datetime

with Switch(datetime.today().weekday()) as case:
    if case(0):
        # Basic usage of switch
        print("I hate mondays so much.")
        # Note there is no break needed here
    elif case(1,2):
        # This switch also supports multiple conditions (in one line)
        print("When is the weekend going to be here?")
    elif case(3,4):
        print("The weekend is near.")
    else:
        # Default would occur here
        print("Let's go have fun!") # Didn't use case for example purposes

@Will 2015-05-03 09:13:53

Using context managers is a good creative solution. I'd recommend adding a bit of explanation and maybe a link to some information on Context Managers to give this post some, well, context ;)

@itsbruce 2017-10-02 08:05:27

I don't like if/elif chains much but this is both the most creative and the most practical of all the solutions I've seen using Python's existing syntax.

@Peter 2019-01-24 11:03:48

This is really nice. One suggested improvement is to add a (public) value property to the Switch class so that you can reference the case.value within the statement.

@John Doe 2011-07-07 06:12:47

My favorite one is a really nice recipe. You'll really like it. It's the closest one I've seen to actual switch case statements, especially in features.

class switch(object):
    def __init__(self, value):
        self.value = value
        self.fall = False

    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration

    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args: # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False

Here's an example:

# The following example is pretty much the exact use-case of a dictionary,
# but is included for its simplicity. Note that you can include statements
# in each suite.
v = 'ten'
for case in switch(v):
    if case('one'):
        print 1
        break
    if case('two'):
        print 2
        break
    if case('ten'):
        print 10
        break
    if case('eleven'):
        print 11
        break
    if case(): # default, could also just omit condition or 'if True'
        print "something else!"
        # No need to break here, it'll stop anyway

# break is used here to look as much like the real thing as possible, but
# elif is generally just as good and more concise.

# Empty suites are considered syntax errors, so intentional fall-throughs
# should contain 'pass'
c = 'z'
for case in switch(c):
    if case('a'): pass # only necessary if the rest of the suite is empty
    if case('b'): pass
    # ...
    if case('y'): pass
    if case('z'):
        print "c is lowercase!"
        break
    if case('A'): pass
    # ...
    if case('Z'):
        print "c is uppercase!"
        break
    if case(): # default
        print "I dunno what c was!"

# As suggested by Pierre Quentel, you can even expand upon the
# functionality of the classic 'case' statement by matching multiple
# cases in a single shot. This greatly benefits operations such as the
# uppercase/lowercase example above:
import string
c = 'A'
for case in switch(c):
    if case(*string.lowercase): # note the * for unpacking as arguments
        print "c is lowercase!"
        break
    if case(*string.uppercase):
        print "c is uppercase!"
        break
    if case('!', '?', '.'): # normal argument passing style also applies
        print "c is a sentence terminator!"
        break
    if case(): # default
        print "I dunno what c was!"

@Ski 2013-12-12 16:24:51

I would substitute for case in switch() with with switch() as case, makes more sense, since it need s to run only once.

@Jonas Schäfer 2014-05-08 16:53:10

@Skirmantas: Note that with doesn’t allow for break though, so the fallthrough option is taken away.

@David Winiecki 2014-09-12 15:47:16

Apologies for not putting more effort in to determine this myself: a similar answer above is not thread safe. Is this?

@Jasen 2016-08-23 22:09:30

@DavidWiniecki The code components missing from the above (and possibly copyright by activestate) appear to be thread safe.

@mpag 2016-10-31 19:08:18

would another version of this be something like if c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"?

@Felix Martinez 2019-02-06 08:49:31

you can use a dispatched dict:

#!/usr/bin/env python


def case1():
    print("This is case 1")

def case2():
    print("This is case 2")

def case3():
    print("This is case 3")


token_dict = {
    "case1" : case1,
    "case2" : case2,
    "case3" : case3,
}


def main():
    cases = ("case1", "case3", "case2", "case1")
    for case in cases:
        token_dict[case]()


if __name__ == '__main__':
    main()

Output:

This is case 1
This is case 3
This is case 2
This is case 1

@Vikhyat Agarwal 2018-09-28 17:11:33

def f(x):
    dictionary = {'a':1, 'b':2, 'c':3}
    return dictionary.get(x,'Not Found') 
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary

@Henry Woody 2018-09-28 19:28:39

Consider including a short description of your code and how it solves the posted question

@Vikhyat Agarwal 2018-10-30 17:22:24

Okay, I've added a comment for that now.

@Woody1193 2018-08-30 22:34:49

There have been a lot of answers so far that have said, "we don't have a switch in Python, do it this way". However, I would like to point out that the switch statement itself is an easily-abused construct that can and should be avoided in most cases because they promote lazy programming. Case in point:

def ToUpper(lcChar):
    if (lcChar == 'a' or lcChar == 'A'):
        return 'A'
    elif (lcChar == 'b' or lcChar == 'B'):
        return 'B'
    ...
    elif (lcChar == 'z' or lcChar == 'Z'):
        return 'Z'
    else:
        return None        # or something

Now, you could do this with a switch-statement (if Python offered one) but you'd be wasting your time because there are methods that do this just fine. Or maybe, you have something less obvious:

def ConvertToReason(code):
    if (code == 200):
        return 'Okay'
    elif (code == 400):
        return 'Bad Request'
    elif (code == 404):
        return 'Not Found'
    else:
        return None

However, this sort of operation can and should be handled with a dictionary because it will be faster, less complex, less prone to error and more compact.

And the vast majority of "use cases" for switch statements will fall into one of these two cases; there's just very little reason to use one if you've thought about your problem thoroughly.

So, rather than asking "how do I switch in Python?", perhaps we should ask, "why do I want to switch in Python?" because that's often the more interesting question and will often expose flaws in the design of whatever you're building.

Now, that isn't to say that switches should never be used either. State machines, lexers, parsers and automata all use them to some degree and, in general, when you start from a symmetrical input and go to an asymmetrical output they can be useful; you just need to make sure that you don't use the switch as a hammer because you see a bunch of nails in your code.

@elp 2010-12-06 15:08:12

If you're searching extra-statement, as "switch", I built a python module that extends Python. It's called ESPY as "Enhanced Structure for Python" and it's available for both Python 2.x and Python 3.x.

For example, in this case, a switch statement could be performed by the following code:

macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

that can be used like this:

a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

so espy translate it in Python as:

a=3
while True:
    cont=False
    if a==0 or cont:
        cont=True
        print ("Zero")
    if a==1 or cont:
        cont=True
        print ("Smaller than 2")
        break
    print ("greater than 1")
    break

@ArtOfWarfare 2014-06-29 12:56:50

Very cool, but what's the point of the while True: at the top of the generated Python code? It'll inevitably hit the break at the bottom of the generated Python code, so it seems to me that both the while True: and break could be removed. Further, is ESPY smart enough to change the name of cont if the user uses that same name in their own code? In any event, I want to use vanilla Python so I won't use this, but it's cool none-the-less. +1 for sheer coolness.

@Solomon Ucko 2019-03-23 14:10:44

@ArtOfWarfare The reason for the while True: and breaks is to allow but not require fall-through.

@Solomon Ucko 2019-03-24 00:57:56

Is this module still available?

@sudhir tataraju 2018-08-12 18:35:50

Easy to remember:

while True:
    try:
        x = int(input("Enter a numerical input: "))
    except:
        print("Invalid input - please enter a Integer!");
    if x==1:
        print("good");
    elif x==2:
        print("bad");
    elif x==3:
        break
    else:
        print ("terrible");

@Tony Suffolk 66 2018-06-06 16:45:58

A solution I tend to use which also makes use of dictionaries is :

def decision_time( key, *args, **kwargs):
    def action1()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action2()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action3()
        """This function is a closure - and has access to all the arguments"""
        pass

   return {1:action1, 2:action2, 3:action3}.get(key,default)()

This has the advantage that it doesn't try to evaluate the functions every time, and you just have to ensure that the outer function gets all the information that the inner functions need.

@Alex Hall 2018-06-04 20:21:47

Similar to this answer by abarnert, here is a solution specifically for the use case of calling a single function for each 'case' in the switch, while avoiding the lambda or partial for ultra-conciseness while still being able to handle keyword arguments:

class switch(object):
    NO_DEFAULT = object()

    def __init__(self, value, default=NO_DEFAULT):
        self._value = value
        self._result = default

    def __call__(self, option, func, *args, **kwargs):
        if self._value == option:
            self._result = func(*args, **kwargs)
        return self

    def pick(self):
        if self._result is switch.NO_DEFAULT:
            raise ValueError(self._value)

        return self._result

Example usage:

def add(a, b):
    return a + b

def double(x):
    return 2 * x

def foo(**kwargs):
    return kwargs

result = (
    switch(3)
    (1, add, 7, 9)
    (2, double, 5)
    (3, foo, bar=0, spam=8)
    (4, lambda: double(1 / 0))  # if evaluating arguments is not safe
).pick()

print(result)

Note that this is chaining calls, i.e. switch(3)(...)(...)(...). Don't put commas in between. It's also important to put it all in one expression, which is why I've used extra parentheses around the main call for implicit line continuation.

The above example will raise an error if you switch on a value that is not handled, e.g. switch(5)(1, ...)(2, ...)(3, ...). You can provide a default value instead, e.g. switch(5, default=-1)... returns -1.

@abarnert 2018-06-04 18:41:10

As a minor variation on Mark Biek's answer, for uncommon cases like this duplicate where the user has a bunch of function calls to delay with arguments to pack in (and it isn't worth building a bunch of functions out-of-line), instead of this:

d = {
    "a1": lambda: a(1),
    "a2": lambda: a(2),
    "b": lambda: b("foo"),
    "c": lambda: c(),
    "z": lambda: z("bar", 25),
    }
return d[string]()

… you can do this:

d = {
    "a1": (a, 1),
    "a2": (a, 2),
    "b": (b, "foo"),
    "c": (c,)
    "z": (z, "bar", 25),
    }
func, *args = d[string]
return func(*args)

This is certainly shorter, but whether it's more readable is an open question…


I think it might be more readable (although not briefer) to switch from lambda to partial for this particular use:

d = {
    "a1": partial(a, 1),
    "a2": partial(a, 2),
    "b": partial(b, "foo"),
    "c": c,
    "z": partial(z, "bar", 25),
    }
return d[string]()

… which has the advantage of working nicely with keyword arguments as well:

d = {
    "a1": partial(a, 1),
    "a2": partial(a, 2),
    "b": partial(b, "foo"),
    "c": c,
    "k": partial(k, key=int),
    "z": partial(z, "bar", 25),
    }
return d[string]()

@Alex Hall 2018-06-04 19:39:34

The problem with this answer is that all the arguments still get evaluated immediately. Even if this works for the user at first, if one day they have to add a case foo(some_expensive_function(3)) then it's a problem. Either the whole thing breaks or they have to separately test for that case before the dictionary, which not only looks odd but might mislead a reader looking at a glance into thinking that the only options to consider are in the dictionary.

@abarnert 2018-06-04 19:49:16

@AlexHall When you have a hunk of arbitrary code that has to be deferred, rather than just a function call that has to be deferred, you really do need to wrap it in a function. And for anything less trivial than the toy example you just gave, it’s probably not even a lambda, but an out-of-line named def that you want. But I don’t think that’s a flaw in the language or anything, because such code really isn’t—and shouldn’t be—common. If you have a more realistic example, there’s likely a better way to refactor it.

@Alex Hall 2018-06-04 20:24:00

You're right, my example can be solved by just writing lambda: foo(some_expensive_function(3)) for that one case. I've added my own answer along these lines.

@abarnert 2018-04-10 06:13:39

Most of the answers here are pretty old, and especially the accepted ones, so it seems worth updating.

First, the official Python FAQ covers this, and recommends the elif chain for simple cases and the dict for larger or more complex cases. It also suggests a set of visit_ methods (a style used by many server frameworks) for some cases:

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

The FAQ also mentions PEP 275, which was written to get an official once-and-for-all decision on adding C-style switch statements. But that PEP was actually deferred to Python 3, and it was only officially rejected as a separate proposal, PEP 3103. The answer was, of course, no—but the two PEPs have links to additional information if you're interested in the reasons or the history.


One thing that came up multiple times (and can be seen in PEP 275, even though it was cut out as an actual recommendation) is that if you're really bothered by having 8 lines of code to handle 4 cases, vs. the 6 lines you'd have in C or Bash, you can always write this:

if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')

This isn't exactly encouraged by PEP 8, but it's readable and not too unidiomatic.


Over the more than a decade since PEP 3103 was rejected, the issue of C-style case statements, or even the slightly more powerful version in Go, has been considered dead; whenever anyone brings it up on python-ideas or -dev, they're referred to the old decision.

However, the idea of full ML-style pattern matching arises every few years, especially since languages like Swift and Rust have adopted it. The problem is that it's hard to get much use out of pattern matching without algebraic data types. While Guido has been sympathetic to the idea, nobody's come up with a proposal that fits into Python very well. (You can read my 2014 strawman for an example.) This could change with dataclass in 3.7 and some sporadic proposals for a more powerful enum to handle sum types, or with various proposals for different kinds of statement-local bindings (like PEP 3150, or the set of proposals currently being discussed on -ideas). But so far, it hasn't.

There are also occasionally proposals for Perl 6-style matching, which is basically a mishmash of everything from elif to regex to single-dispatch type-switching.

@True 2018-03-19 16:32:29

I've found the following answer from python docs most helpful:

You can do this easily enough with a sequence of if... elif... elif... else. There have been some proposals for switch statement syntax, but there is no consensus (yet) on whether and how to do range tests. See PEP 275 for complete details and the current status.

For cases where you need to choose from a very large number of possibilities, you can create a dictionary mapping case values to functions to call. For example:

def function_1(...):
    ...

functions = {'a': function_1,
             'b': function_2,
             'c': self.method_1, ...}

func = functions[value]
func()

For calling methods on objects, you can simplify yet further by using the getattr() built-in to retrieve methods with a particular name:

def visit_a(self, ...):
    ...
...

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

It’s suggested that you use a prefix for the method names, such as visit_ in this example. Without such a prefix, if values are coming from an untrusted source, an attacker would be able to call any method on your object.

@Peter Mortensen 2018-07-03 09:12:05

PEP 275 seems to be have rejected.

@Ramazan Polat 2018-01-01 04:48:56

Although there are already enough answers, I want to point a simpler and more powerful solution:

class Switch:
    def __init__(self, switches):
        self.switches = switches
        self.between = len(switches[0]) == 3

    def __call__(self, x):
        for line in self.switches:
            if self.between:
                if line[0] <= x < line[1]:
                    return line[2]
            else:
                if line[0] == x:
                    return line[1]
        return None


if __name__ == '__main__':
    between_table = [
        (1, 4, 'between 1 and 4'),
        (4, 8, 'between 4 and 8')
    ]

    switch_between = Switch(between_table)

    print('Switch Between:')
    for i in range(0, 10):
        if switch_between(i):
            print('{} is {}'.format(i, switch_between(i)))
        else:
            print('No match for {}'.format(i))


    equals_table = [
        (1, 'One'),
        (2, 'Two'),
        (4, 'Four'),
        (5, 'Five'),
        (7, 'Seven'),
        (8, 'Eight')
    ]
    print('Switch Equals:')
    switch_equals = Switch(equals_table)
    for i in range(0, 10):
        if switch_equals(i):
            print('{} is {}'.format(i, switch_equals(i)))
        else:
            print('No match for {}'.format(i))

Output:

Switch Between:
No match for 0
1 is between 1 and 4
2 is between 1 and 4
3 is between 1 and 4
4 is between 4 and 8
5 is between 4 and 8
6 is between 4 and 8
7 is between 4 and 8
No match for 8
No match for 9

Switch Equals:
No match for 0
1 is One
2 is Two
No match for 3
4 is Four
5 is Five
No match for 6
7 is Seven
8 is Eight
No match for 9

@M T Head 2017-12-28 20:21:14

And another option:

def fnc_MonthSwitch(int_Month): #### Define a function take in the month variable 
    str_Return ="Not Found"     #### Set Default Value 
    if int_Month==1:       str_Return = "Jan"   
    if int_Month==2:       str_Return = "Feb"   
    if int_Month==3:       str_Return = "Mar"   
    return str_Return;          #### Return the month found  
print ("Month Test 3:  " + fnc_MonthSwitch( 3) )
print ("Month Test 14: " + fnc_MonthSwitch(14) )

@nonrandom_passer 2017-11-16 16:47:52

The following works for my situation when I need a simple switch-case to call a bunch of methods and not to just print some text. After playing with lambda and globals it hit me as the simplest option for me so far. Maybe it will help someone also:

def start():
    print("Start")

def stop():
    print("Stop")

def print_help():
    print("Help")

def choose_action(arg):
    return {
        "start": start,
        "stop": stop,
        "help": print_help,
    }.get(arg, print_help)

argument = sys.argv[1].strip()
choose_action(argument)()  # calling a method from the given string

@Tony Suffolk 66 2018-06-06 16:51:50

make your action functions closures - i.e. define them in your choose_action function - and you can have access to any arguments etc too :-) i.e. it becomes my solution ...

@Semicolon Warrier 2017-08-31 12:36:18

I've made a Switch Case implementation that doesn't quite use ifs externally(it still uses an if in the class).

class SwitchCase(object):
    def __init__(self):
        self._cases = dict()

    def add_case(self,value, fn):
        self._cases[value] = fn

    def add_default_case(self,fn):
        self._cases['default']  = fn

    def switch_case(self,value):
        if value in self._cases.keys():
            return self._cases[value](value)
        else:
            return self._cases['default'](0)

Use it like this:-

from switch_case import SwitchCase
switcher = SwitchCase()
switcher.add_case(1, lambda x:x+1)
switcher.add_case(2, lambda x:x+3)
switcher.add_default_case(lambda _:[1,2,3,4,5])

print switcher.switch_case(1) #2
print switcher.switch_case(2) #5
print switcher.switch_case(123) #[1, 2, 3, 4, 5]

@iBug 2018-03-27 09:00:09

Get a performance boost if you replace if value in keys with a try-except block.

@user2233949 2017-08-14 22:19:38

I'm just going to drop my two cents in here. The reason there isn't a case/switch statement in Python is because Python follows the principle of 'Theres only one right way to do something'. So obviously you could come up with various ways of recreating switch/case functionality, but the Pythonic way of accomplishing this is the if/elif construct. ie

if something:
    return "first thing"
elif somethingelse:
    return "second thing"
elif yetanotherthing:
    return "third thing"
else:
    return "default thing"

I just felt PEP 8 deserved a nod here. One of the beautiful things about Python is its simplicity and elegance. That is largely derived from principles laid our in PEP 8, including "There's only one right way to do something"

@itsbruce 2017-10-01 10:01:28

So why does Python have for loops and while loops? Everything you can do with a for loop you can implement with a while loop.

@user228395 2017-10-06 12:34:27

True. Switch/case are too often abused by beginning programmers. What they really want is the strategy pattern.

@T.W.R. Cole 2018-02-16 05:17:02

Sounds like Python wishes it was Clojure

@Taylor 2018-05-09 16:52:28

@T.W.R.Cole I don't think so, Python was doing it first. Python has been around since 1990 and Clojure since 2007.

@T.W.R. Cole 2018-08-23 19:09:34

There's only one right way to do something. Python 2.7 or Python 3? Lol.

@user2233949 2018-08-24 20:55:14

Obviously a design principle is not a declaration of perfection

@Solomon Ucko 2019-03-24 00:55:32

The quote is from PEP 20, the Zen of Python: "There should be one-- and preferably only one --obvious way to do it.".

@simpleuser 2019-08-15 01:33:39

No fallthru support

@damirlj 2017-08-06 08:53:39

Switch statement is just syntactic sugar for if/elif/else. What any control statement is doing is delegating the job based on certain condition is being fulfilled - decision path.For wrapping that into module and being able to call a job based on it's unique id, one can utilizes on inheritance and the fact that any method in python is virtual, to provide the derived class specific job implementation, as specific "case" handler

#!/usr/bin/python

import sys

class Case(object):
    """
        Base class wich specifies the interface for "case" handler.
        The all required arbitrary arguments inside "execute" method will be
        provided through the derived class
        specific constructor

        @note in python, all class methods are virtual
    """
    def __init__(self, id):
        self.id = id

    def pair(self):
        """
            Pairs the given id of the "case" with
            the instance on which "execute" will be called
        """
        return (self.id, self)

    def execute(self):#base class virtual method that needs to be overrided
        pass

class Case1(Case):
    def __init__(self, id, msg):
        self.id = id
        self.msg = msg
    def execute(self):#override the base class method
        print("<Case1> id={}, message: \"{}\"".format(str(self.id), self.msg))

class Case2(Case):
    def __init__(self, id, n):
        self.id = id
        self.n = n
    def execute(self):#override the base class method
        print("<Case2> id={}, n={}.".format(str(self.id), str(self.n)))
        print("\n".join(map(str, range(self.n))))



class Switch(object):
    """
        The class wich delegates the jobs
        based on the given job id
    """
    def __init__(self, cases):
        self.cases = cases#dictionary: time complexitiy for access operation is 1
    def resolve(self, id):

        try:
            cases[id].execute()
        except KeyError as e:
            print("Given id: {} is wrong!".format(str(id)))



if __name__ == '__main__':

    # Cases
    cases=dict([Case1(0, "switch").pair(), Case2(1, 5).pair()])

    switch = Switch(cases)

    # id will be dynamically specified
    switch.resolve(0)
    switch.resolve(1)
    switch.resolve(2)

@Nick 2008-09-19 15:45:15

If you'd like defaults you could use the dictionary get(key[, default]) method:

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found

@John Mee 2010-04-09 07:57:40

What if 'a' and 'b' match 1, and 'c' and 'd' match 2?

@Nick 2010-04-09 09:54:55

@JM: Well, obviously dictionary lookups don't support fall-throughs. You could do a double dictionary lookup. I.e. 'a' & 'b' point to answer1 and 'c' and 'd' point to answer2, which are contained in a second dictionary.

@HaTiMSuM 2016-10-13 11:01:54

this is better to pass a default value

@Idan Haim Shalom 2017-08-30 08:01:47

There is a problems with this approach, first each time you call f you going to create the dict again second if you have more complex value you can get an exceptions ex. if x is a tuple and we are want to do something like this x = ('a') def f(x): return { 'a': x[0], 'b': x[1] }.get(x[0], 9) This will raise IndexError

@Nick 2017-09-19 21:59:49

@Idan: The question was to replicate switch. I'm sure I could break this code too if I tried putting in odd values. Yes, it will recreate, but it is simple to fix.

@Yster 2017-06-14 12:39:34

I was quite confused after reading the answer, but this cleared it all up:

def numbers_to_strings(argument):
    switcher = {
        0: "zero",
        1: "one",
        2: "two",
    }
    return switcher.get(argument, "nothing")

This code is analogous to:

function(argument){
    switch(argument) {
        case 0:
            return "zero";
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "nothing";
    }
}

Check the Source for more about dictionary mapping to functions.

@Tom 2017-04-21 07:17:38

Expanding on Greg Hewgill's answer - We can encapsulate the dictionary-solution using a decorator:

def case(callable):
    """switch-case decorator"""
    class case_class(object):
        def __init__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs

        def do_call(self):
            return callable(*self.args, **self.kwargs)

return case_class

def switch(key, cases, default=None):
    """switch-statement"""
    ret = None
    try:
        ret = case[key].do_call()
    except KeyError:
        if default:
            ret = default.do_call()
    finally:
        return ret

This can then be used with the @case-decorator

@case
def case_1(arg1):
    print 'case_1: ', arg1

@case
def case_2(arg1, arg2):
    print 'case_2'
    return arg1, arg2

@case
def default_case(arg1, arg2, arg3):
    print 'default_case: ', arg1, arg2, arg3

ret = switch(somearg, {
    1: case_1('somestring'),
    2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))

print ret

The good news are that this has already been done in NeoPySwitch-module. Simply install using pip:

pip install NeoPySwitch

@guneysus 2014-11-30 10:12:36

I liked Mark Bies's answer

Since the x variable must used twice, I modified the lambda functions to parameterless.

I have to run with results[value](value)

In [2]: result = {
    ...:   'a': lambda x: 'A',
    ...:   'b': lambda x: 'B',
    ...:   'c': lambda x: 'C'
    ...: }
    ...: result['a']('a')
    ...: 
Out[2]: 'A'

In [3]: result = {
    ...:   'a': lambda : 'A',
    ...:   'b': lambda : 'B',
    ...:   'c': lambda : 'C',
    ...:   None: lambda : 'Nothing else matters'

    ...: }
    ...: result['a']()
    ...: 
Out[3]: 'A'

Edit: I noticed that I can use None type with with dictionaries. So this would emulate switch ; case else

@Bob Stein 2015-03-11 15:19:57

Doesn't the None case emulate simply result[None]() ?

@guneysus 2015-03-11 15:52:54

Yes, exactly. I mean result = {'a': 100, None:5000}; result[None]

@Bob Stein 2015-03-11 16:54:50

Just checking that no one is thinking None: behaves like default:.

@dccsillag 2016-05-25 22:56:30

If you don't worry losing syntax highlight inside the case suites, you can do the following:

exec {
    1: """
print ('one')
""", 
    2: """
print ('two')
""", 
    3: """
print ('three')
""",
}.get(value, """
print ('None')
""")

Where value is the value. In C, this would be:

switch (value) {
    case 1:
        printf("one");
        break;
    case 2:
        printf("two");
        break;
    case 3:
        printf("three");
        break;
    default:
        printf("None");
        break;
}

We can also create a helper function to do this:

def switch(value, cases, default):
    exec cases.get(value, default)

So we can use it like this for the example with one, two and three:

switch(value, {
    1: """
print ('one')
    """, 
    2: """
print ('two')
    """, 
    3: """
print ('three')
    """,
}, """
print ('None')
""")

@J_Zar 2016-03-18 08:01:35

I think the best way is to use the python language idioms to keep your code testable. As showed in previous answers, I use dictionaries to take advantage of python structures and language and keep the "case" code isolated in different methods. Below there is a class, but you can use directly a module, globals and functions. The class has methods that can be tested with isolation. Dependending to your needs, you can play with static methods and attributes too.

class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
            "CHOICE1" : self.my_func1,
            "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

It is possible to take advantage of this method using also classes as keys of "__choice_table". In this way you can avoid isinstance abuse and keep all clean and testable.

Supposing you have to process a lot of messages or packets from the net or your MQ. Every packet has its own structure and its management code (in a generic way). With the above code it is possible to do something like this:

class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

So complexity is not spread in the code flow but it is rendered in code structure.

@jmcollin92 2016-04-08 13:25:03

Really ugly... switch case is so clean when reading. Cannot understand why it is not implemented in Python.

@J_Zar 2016-04-11 06:51:37

@AndyClifton: I'm sorry... an example? Think of every time you need to have multiple decision branching code and you can apply this method.

@J_Zar 2016-04-11 06:53:38

@jmcollin92: the switch statement is confortable, I agree. However the programmer tends to write very long statements and code which is not reusable. The way I described is cleaner to test and more reusable, IMHO.

@Andy Clifton 2016-04-11 09:14:07

@J_Zar : re. My request for an example: yes, I get that, but I'm struggling to put this into the context of a larger piece of code. Could you show how I might use this in a real world situation?

@J_Zar 2016-04-20 14:56:11

@AndyClifton: I'm sorry, I'm late but I posted some example case.

@Eugene 2008-09-13 00:43:49

If you are really just returning a predetermined, fixed value, you could create a dictionary with all possible input indexes as the keys, along with their corresponding values. Also, you might not really want a function to do this - unless you're computing the return value somehow.

Oh, and if you feel like doing something switch-like, see here.

@user5224656 2015-08-13 17:40:47

# simple case alternative

some_value = 5.0

# this while loop block simulates a case block

# case
while True:

    # case 1
    if some_value > 5:
        print ('Greater than five')
        break

    # case 2
    if some_value == 5:
        print ('Equal to five')
        break

    # else case 3
    print ( 'Must be less than 5')
    break

@ChaimG 2015-06-17 02:25:10

My favorite Python recipe for switch/case is:

choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')

Short and simple for simple scenarios.

Compare to 11+ lines of C code:

// C Language version of a simple 'switch/case'.
switch( key ) 
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

You can even assign multiple variables by using tuples:

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))

@cerd 2015-08-18 23:16:50

I find this to be a more robust answer than the accepted.

@some user 2016-02-29 19:05:10

Note that the 2 segments of code aren't the same. The python version returns 'default' when no match but the C version returns -1. When you replace the line as choices.get(key, -1), it becomes harder to read because people have to pause to think how get() works and what -1 mean.

@ChaimG 2016-03-01 00:17:10

@some user: C requires that the return value be the same type for all cases. Python does not. I wanted to highlight this flexibility of Python just in case someone had a situation that warranted such usage.

@ChaimG 2016-03-01 00:19:26

@some user: Personally, I find {}.get(,) readable. For extra readability for Python beginners you may want to use default = -1; result = choices.get(key, default).

@Jasen 2016-08-23 22:01:57

compare with 1 line of c++ result=key=='a'?1:key==b?2:-1

@ChaimG 2016-08-24 02:57:05

@Jasen one can argue that you can do it in one line of Python as well: result = 1 if key == 'a' else (2 if key == 'b' else 'default'). but is the one liner readable?

@Valen 2017-06-06 11:22:52

@ChaimG tbh C one liner is fairly readeable under these simple cases

@Peter 2017-09-11 15:37:51

An additional benefit of this approach is that the code and the data are more clearly separated. This make it easier to pass the data as a parameter (simplifies the task of writing tests) or move it into a configuration file, for example.

@Rakurai 2020-01-06 14:51:43

Such is the trouble with comparing languages by how many lines it takes to express an idea. You can usually accomplish something in Python with one line, and you can always express something with one line in C.

Related Questions

Sponsored Content

42 Answered Questions

[SOLVED] How do I merge two dictionaries in a single expression?

23 Answered Questions

[SOLVED] Does Python have a ternary conditional operator?

17 Answered Questions

[SOLVED] What are metaclasses in Python?

32 Answered Questions

[SOLVED] "Least Astonishment" and the Mutable Default Argument

10 Answered Questions

[SOLVED] Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3?

63 Answered Questions

[SOLVED] Calling an external command from Python

24 Answered Questions

[SOLVED] How to write a switch statement in Ruby

19 Answered Questions

[SOLVED] How to import other Python files?

10 Answered Questions

[SOLVED] Does Python have a string 'contains' substring method?

19 Answered Questions

[SOLVED] Python progression path - From apprentice to guru

  • 2010-04-04 00:28:33
  • Morlock
  • 363150 View
  • 659 Score
  • 19 Answer
  • Tags:   python

Sponsored Content