аватар danmodenov@mail.ru · 01.01.1970 03:00

Есть ли в Python оператор switch case?

n

Столкнулся с тем, что требуется реализовать множественное условие, которое в других языках я бы реализовал с помощью конструкции switch-case.

n

В Python мне приходится расписывать всё через условия if-elif-else. Это мне кажется довольно неудобным.

n

Есть ли более удобный способ записи подобных условий?

n

Например, у меня есть единицы измерения и в зависимости от выбранной мне нужно вернуть соответствующий множитель:

n
def get_multiplier(unit):n    if unit == 'mm':n        retu 10**-3n    if unit == 'cm':n        retu 10**-2n    if unit == 'dm':n        retu 10**-1n    if unit == 'm':n        retu 1n    if unit == 'km':n        retu 10**3n    raise ValueError('Undefined unit: {}'.format(unit))n
n
аватар danmodenov@mail.ru · 01.01.1970 03:00

UPD END

Для начала, ничего особенно плохого в использовании конструкции if-elif-else нет.

При желании можно найти несколько альтернатив.


Использование словарей

Довольно распространённый способ организации конструкции switch-case в Python — это использование словаря. Проще показать на примере:

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

Для того, чтобы получить нужный множитель в этом случае требуется лишь взять значение по ключу:

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

Если вы твёрдо уверены, что значение всегда будет присутствовать в словаре, можете опустить блок try-except и быть готовым ловить исключение в другом месте.

Некоторой вариацией этого подходя будет предварительная проверка значения в условии:

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

В Python принято использовать подход, звучащий примерно так: "лучше попробовать и получить ошибку, чем каждый раз спрашивать разрешение", поэтому более предпочтительный подход с использованием исключений.

Если хочется использовать значение по умолчанию в случае, если ключ отсутствует, удобно использовать метод get:

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

Если словарь вам требуется один раз, можно объединить эти выражения в одно:

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

На этом возможности этого подхода не заканчиваются. Можно использовать условные выражения в качестве ключей словаря:

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

Этот словарь после вычисления будет иметь два ключа True и False. Нас интересует ключ True. Будьте внимательны, что условия не перекрываются!

Подобные словари могут проверять произвольное свойство, например, тип ():

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

При необходимости более сложных действий хранить функцию в качестве значения по каждому ключу:

import operatoroperations = {    '+': operator.add,    '*': lambda x, y: x * y,    # ...}def calc(operation, a, b):     retu operations[operation](a, b)

Будьте внимательны при использовании словаря с функциями — убедитесь, что вы не вызываете эти функции внутри словаря, а передаёте по ключу; иначе все функции будут выполняться каждый раз при конструировании словаря.


Другие способы

Приведены скорее для ознакомления, чем для реального использования.

  1. Создадим класс, в котором напишем несколько методов вида:

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

    И один метод-диспетчер:

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

    После этого можно использовать метод dispatch для выполнения соответствующей функции, передавая её суффикс, например x.dispatch('first').

  2. Использование специальных классов

    Если есть желание использовать синтаксис switch-case в максимально похожем стиле, можно написать что-то вроде следующего :

     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             retu True         elif self.value in args:             self.fall = True             retu True         retu False

    Используется следующим образом:

     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. .

    Довольно небезопасный способ, см. пример:

     # Условная конструкция 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')

    Этот способ использует короткую схему вычисления операторов and и or, т.е. то, что логические выражения вычисляются следующим образом:

    (t вычисляющееся как Truef вычисляется как False):

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

    П��едложенный способ вычислений будет работать только в том случае, если второй аргумент оператора and всегда будет содержать True-выражение, иначе этот блок всегда будет пропускаться. Например:

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

    Будет работать неправильно в случае 0 <= x < 1, т.к. выражение 0 <= x < 1 and 0 равно 0, и из-за этого управление перейдёт следующему аргументу or, вместо того, чтобы вернуть этот ноль в качестве результата выражения.

  4. Если объявить несколько функций:

     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:         retu exс_class    retu None

    Здесь используется функция , которая возвращает набор из информации об обрабатываемом исключении: класса, экземпляра и стека.

    Код с использованием этих конструкций будет выглядеть следующим образом:

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

Последние

Похожие