Is there a switch case statement in Python?

Question:

Faced with the fact that it is required to implement a multiple condition, which in other languages ​​I would implement using the switch-case construct.

In Python, I have to write everything through if-elif-else . This seems rather inconvenient to me.

Is there a better way to write such conditions?

For example, I have units of measurement and, depending on the selected one, I need to return the appropriate multiplier:

def get_multiplier(unit):
    if unit == 'mm':
        return 10**-3
    if unit == 'cm':
        return 10**-2
    if unit == 'dm':
        return 10**-1
    if unit == 'm':
        return 1
    if unit == 'km':
        return 10**3
    raise ValueError('Undefined unit: {}'.format(unit))

Answer:

For starters, there is nothing particularly wrong with using the if-elif-else .

Several alternatives can be found if desired.


Using dictionaries

A fairly common way to organize a switch-case construct in Python is to use a dictionary. It's easier to show with an example:

unit_to_multiplier = {
    'mm': 10**-3,
    'cm': 10**-2,
    'dm': 10**-1,
    'm': 1,
    'km': 10**3
}

In order to get the desired multiplier in this case, you only need to take the value by key:

try:
    mult = unit_to_multiplier['cm']
except KeyError as e:
    # можно также присвоить значение по умолчанию вместо бросания исключения
    raise ValueError('Undefined unit: {}'.format(e.args[0]))

If you're pretty sure the value will always be in the dictionary, you can omit the try-except block and be prepared to catch the exception elsewhere.

A variation on this approach would be to pre-check the value in the condition:

if unit in unit_to_multiplier:
    mult = unit_to_multiplier[unit]
else:
    # обработка отсутствия значения в словаре

In Python, it's common to take an approach that sounds something like "it's better to try and get an error than to ask permission every time", so the exception approach is more preferable.

If you want to use a default value in case the key is missing, it's convenient to use the get method:

mult = unit_to_multiplier.get('ultra-meter', 0)

If you only need the dictionary once, you can combine these expressions into one:

unit_to_multiplier = {
    'mm': 10**-3,
    'cm': 10**-2,
    'dm': 10**-1,
    'm': 1,
    'km': 10**3
}.get('km', 0)

The possibilities of this approach do not end there. You can use conditional expressions as dictionary keys:

def get_temp_description(temp):
    return {
               temp < -20: 'Холодно',
        -20 <= temp < 0:   'Прохладно',
          0 <= temp < 15:  'Зябко',
         15 <= temp < 25:  'Тепло',
         25 <= temp:       'Жарко'
    }[True]

This dictionary after calculation will have two keys True and False . We are interested in the True key. Be careful that the conditions do not overlap !

Dictionaries like this can check for an arbitrary property, such as a type ( example source ):

selector = {
    type(x) == str  : "it's a str",
    type(x) == tuple: "it's a tuple",
    type(x) == dict : "it's a dict"
}[1]   # можно использовать число 1 как синоним True

If you need more complex actions, store the function as a value for each key:

import operator

operations = {
    '+': operator.add,
    '*': lambda x, y: x * y,
    # ...
}

def calc(operation, a, b): 
    return operations[operation](a, b)

Be careful when using a dictionary with functions – make sure that you do not call these functions inside the dictionary, but pass by key; otherwise, all functions will be executed each time the dictionary is constructed.


other methods

They are provided for informational purposes rather than actual use.

  1. Using functions with wildcard names

    Let's create a class in which we will write several view methods:

     def process_first(self): ... def process_second(self): ... ...

    And one dispatcher method:

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

    You can then use the dispatch method to execute the appropriate function, passing its suffix, such as x.dispatch('first') .

  2. Using Special Classes

    If you want to use switch-case syntax in the most similar style, you can write something like the following code :

     class switch(object): def __init__(self, value): self.value = value # значение, которое будем искать self.fall = False # для пустых case блоков def __iter__(self): # для использования в цикле for """ Возвращает один раз метод match и завершается """ yield self.match raise StopIteration def match(self, *args): """ Указывает, нужно ли заходить в тестовый вариант """ if self.fall or not args: # пустой список аргументов означает последний блок case # fall означает, что ранее сработало условие и нужно заходить # в каждый case до первого break return True elif self.value in args: self.fall = True return True return False

    Used like this:

     x = int(input()) for case in switch(x): if case(1): pass if case(2): pass if case(3): print('Число от 1 до 3') break if case(4): print('Число 4') if case(): # default print('Другое число')
  3. Using the and and or operators .

    Pretty unsafe way, see example:

     # Условная конструкция Select Case x Case x<0 : y = -1 Case 0<=x<1 : y = 0 Case 1<=x<2 : y = 1 Case 2<=x<3 : y = 2 Case Else : y = 'n/a' End Select # Эквивалентная реализация на Python y = (( x < 0 and 'first segment') or (0 <= x < 1 and 'second segment') or (1 <= x < 2 and 'third segment') or (2 <= x < 3 and 'fourth segment') or 'other segment')

    This method uses a short scheme for evaluating the and and or operators, i.e. that boolean expressions are evaluated like this:

    ( t evaluates to True , f evaluates to False ):

     f and x = f t and x = x f or x = x t or x = t

    The proposed calculation method will work only if the second argument of the and operator will always contain a True -expression, otherwise this block will always be skipped. For instance:

     y = (( x < 0 and -1) or (0 <= x < 1 and 0) or (1 <= x < 2 and 1) or (2 <= x < 3 and 2) or 'n/a')

    Will not work correctly in the case of 0 <= x < 1 , because the expression 0 <= x < 1 and 0 is 0 , and because of this, control will jump to the next argument or , instead of returning that zero as the result of the expression.

  4. Using exceptions

    If you declare multiple functions:

     import sys class case_selector(Exception): def __init__(self, value): # один обязательный аргумент Exception.__init__(self, value) def switch(variable): raise case_selector(variable) def case(value): exc_сlass, exс_obj, _ = sys.exc_info() if exc_сlass is case_selector and exс_obj.args[0] == value: return exс_class return None

    This uses the sys.exc_info function, which returns a set of information about the handled exception: class, instance, and stack.

    The code using these constructs would look like this:

     n = int(input()) try: switch(n) except ( case(1), case(2), case(3) ): print "Число от 1 до 3" except case(4): print "Число 4" except: print "Другое число"
Scroll to Top