Tutorial :Replacements for switch statement in Python?



Question:

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?


Solution:1

You could use a dictionary:

def f(x):      return {          'a': 1,          'b': 2,      }[x]  


Solution:2

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  


Solution:3

I've always liked doing it this way

result = {    'a': lambda x: x * 5,    'b': lambda x: x + 7,    'c': lambda x: x - 2  }[value](x)  

From here


Solution:4

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.


Solution:5

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'))  


Solution:6

class switch(object):      value = None      def __new__(class_, value):          class_.value = value          return True    def case(*args):      return any((arg == switch.value for arg in args))  

Usage:

while switch(n):      if case(0):          print "You typed zero."          break      if case(1, 4, 9):          print "n is a perfect square."          break      if case(2):          print "n is an even number."      if case(2, 3, 5, 7):          print "n is a prime number."          break      if case(6, 8):          print "n is an even number."          break      print "Only single-digit numbers are allowed."      break  

Tests:

n = 2  #Result:  #n is an even number.  #n is a prime number.  n = 11  #Result:  #Only single-digit numbers are allowed.  


Solution:7

There's a pattern that I learned from Twisted Python code.

class SMTP:      def lookupMethod(self, command):          return getattr(self, 'do_' + command.upper(), None)      def do_HELO(self, rest):          return 'Howdy ' + rest      def do_QUIT(self, rest):          return 'Bye'    SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'  SMTP().lookupMethod('QUIT')('') # => 'Bye'  

You can use it any time you need to dispatch on a token and execute extended piece of code. In a state machine you would have state_ methods, and dispatch on self.state. This switch can be cleanly extended by inheriting from base class and defining your own do_ methods. Often times you won't even have do_ methods in the base class.

Edit: how exactly is that used

In case of SMTP you will receive HELO from the wire. The relevant code (from twisted/mail/smtp.py, modified for our case) looks like this

class SMTP:      # ...        def do_UNKNOWN(self, rest):          raise NotImplementedError, 'received unknown command'        def state_COMMAND(self, line):          line = line.strip()          parts = line.split(None, 1)          if parts:              method = self.lookupMethod(parts[0]) or self.do_UNKNOWN              if len(parts) == 2:                  return method(parts[1])              else:                  return method('')          else:              raise SyntaxError, 'bad syntax'    SMTP().state_COMMAND('   HELO   foo.bar.com  ') # => Howdy foo.bar.com  

You'll receive ' HELO foo.bar.com ' (or you might get 'QUIT' or 'RCPT TO: foo'). This is tokenized into parts as ['HELO', 'foo.bar.com']. The actual method lookup name is taken from parts[0].

(The original method is also called state_COMMAND, because it uses the same pattern to implement a state machine, i.e. getattr(self, 'state_' + self.mode))


Solution:8

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.

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!"  


Solution:9

class Switch:      def __init__(self, value): self._val = value      def __enter__(self): return self      def __exit__(self, type, value, traceback): return False # Allows traceback to occur      def __call__(self, *mconds): return self._val in mconds    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  


Solution:10

Let's say you don't want to just return a value, but want to use methods that change something on an object. Using the approach stated here would be:

result = {    'a': obj.increment(x),    'b': obj.decrement(x)  }.get(value, obj.default(x))  

What happens here is that python evaluates all methods in the dictionary. So even if your value is 'a', the object will get incremented and decremented by x.

Solution:

func, args = {    'a' : (obj.increment, (x,)),    'b' : (obj.decrement, (x,)),  }.get(value, (obj.default, (x,)))    result = func(*args)  

So you get a list containing a function and its arguments. This way, only the function pointer and the argument list get returned, not evaluated. 'result' then evaluates the returned function call.


Solution:11

expanding on the "dict as switch" idea. if you want to use a default value for your switch:

def f(x):      try:          return {              'a': 1,              'b': 2,          }[x]      except KeyError:          return 'default'  


Solution:12

If you have a complicated case block you can consider using a function dictionary lookup table...

If you haven't done this before its a good idea to step into your debugger and view exactly how the dictionary looks up each function.

NOTE: Do not use "()" inside the case/dictionary lookup or it will call each of your functions as the dictionary / case block is created. Remember this because you only want to call each function once using a hash style lookup.

def first_case():      print "first"    def second_case():      print "second"    def third_case():      print "third"    mycase = {  'first': first_case, #do not use ()  'second': second_case, #do not use ()  'third': third_case #do not use ()  }  myfunc = mycase['first']  myfunc()  


Solution:13

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"


Solution:14

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  


Solution:15

I found that a common switch structure:

switch ...parameter...  case p1: v1; break;  case p2: v2; break;  default: v3;  

can be expressed in Python as follows:

(lambda x: v1 if p1(x) else v2 if p2(x) else v3)  

or formatted in a clearer way:

(lambda x:       v1 if p1(x) else       v2 if p2(x) else       v3)  

Instead of being a statement, the python version is an expression, which evaluates to a value.


Solution:16

I didn't find the simple answer I was looking for anywhere on Google search. But I figured it out anyway. It's really quite simple. Decided to post it, and maybe prevent a few less scratches on someone else's head. The key is simply "in" and tuples. Here is the switch statement behavior with fall-through, including RANDOM fall-through.

l = ['Dog', 'Cat', 'Bird', 'Bigfoot',       'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']    for x in l:      if x in ('Dog', 'Cat'):          x += " has four legs"      elif x in ('Bat', 'Bird', 'Dragonfly'):          x += " has wings."      elif x in ('Snake',):          x += " has a forked tongue."      else:          x += " is a big mystery by default."      print(x)    print()    for x in range(10):      if x in (0, 1):          x = "Values 0 and 1 caught here."      elif x in (2,):          x = "Value 2 caught here."      elif x in (3, 7, 8):          x = "Values 3, 7, 8 caught here."      elif x in (4, 6):          x = "Values 4 and 6 caught here"      else:          x = "Values 5 and 9 caught in default."      print(x)  

Provides:

Dog has four legs  Cat has four legs  Bird has wings.  Bigfoot is a big mystery by default.  Dragonfly has wings.  Snake has a forked tongue.  Bat has wings.  Loch Ness Monster is a big mystery by default.    Values 0 and 1 caught here.  Values 0 and 1 caught here.  Value 2 caught here.  Values 3, 7, 8 caught here.  Values 4 and 6 caught here  Values 5 and 9 caught in default.  Values 4 and 6 caught here  Values 3, 7, 8 caught here.  Values 3, 7, 8 caught here.  Values 5 and 9 caught in default.  


Solution:17

The solutions I use:

A combination of 2 of the solutions posted here, which is relatively easy to read and supports defaults.

result = {    'a': lambda x: x * 5,    'b': lambda x: x + 7,    'c': lambda x: x - 2  }.get(whatToUse, lambda x: x - 22)(value)  

where

.get('c', lambda x: x - 22)(23)  

looks up "lambda x: x - 2" in the dict and uses it with x=23

.get('xxx', lambda x: x - 22)(44)  

doesn't find it in the dict and uses the default "lambda x: x - 22" with x=44.


Solution:18

# 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  


Solution:19

I have made a (relatively) flexible and re-usable solution for this. It can be found at GitHub as this gist. If the result of the switch function is callable, it is automatically called.


Solution:20

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


Solution:21

def f(x):       return 1 if x == 'a' else\              2 if x in 'bcd' else\              0 #default  

Short and easy to read, has a default value and supports expressions in both conditions and return values.

However, it is less efficient than the solution with a dictionary. For example, Python has to scan through all the conditions before returning the default value.


Solution:22

Defining:

def switch1(value, options):    if value in options:      options[value]()  

allows you to use a fairly straightforward syntax, with the cases bundled into a map:

def sample1(x):    local = 'betty'    switch1(x, {      'a': lambda: print("hello"),      'b': lambda: (        print("goodbye," + local),        print("!")),      })  

I kept trying to redefine switch in a way that would let me get rid of the "lambda:", but gave up. Tweaking the definition:

def switch(value, *maps):    options = {}    for m in maps:      options.update(m)    if value in options:      options[value]()    elif None in options:      options[None]()  

Allowed me to map multiple cases to the same code, and to supply a default option:

def sample(x):    switch(x, {      _: lambda: print("other")       for _ in 'cdef'      }, {      'a': lambda: print("hello"),      'b': lambda: (        print("goodbye,"),        print("!")),      None: lambda: print("I dunno")      })  

Each replicated case has to be in its own dictionary; switch() consolidates the dictionaries before looking up the value. It's still uglier than I'd like, but it has the basic efficiency of using a hashed lookup on the expression, rather than a loop through all the keys.


Solution:23

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.


Solution:24

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.


Solution:25

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')  """)  


Solution:26

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.


Solution:27

Inspired by this awesome answer. Requires no other code. Not tested. Realized that fall through does not work properly.

for case in [expression]:      if case == 1:          do_stuff()          # Fall through        if case in range(2, 5):          do_other_stuff()          break        do_default()  


Solution:28

I would just use if/elif/else statements. I think that it's good enough to replace the switch statement.


Solution:29

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  


Solution:30

I made this small and clean solution

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

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


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »