Страницы

Поиск по вопросам

воскресенье, 24 ноября 2019 г.

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


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

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

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

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

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

    


Ответы

Ответ 1



Для начала, ничего особенно плохого в использовании конструкции 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): return { 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 operator operations = { '+': operator.add, '*': lambda x, y: x * y, # ... } def calc(operation, a, b): return operations[operation](a, b) Будьте внимательны при использовании словаря с функциями — убедитесь, что вы не вызывает эти функции внутри словаря, а передаёте по ключу; иначе все функции будут выполнятьс каждый раз при конструировании словаря. Другие способы Приведены скорее для ознакомления, чем для реального использования. Использование функций с шаблонными именами Создадим класс, в котором напишем несколько методов вида: def process_first(self): ... def process_second(self): ... ... И один метод-диспетчер: def dispatch(self, value): method_name = 'process_' + str(value) method = getattr(self, method_name) return method() После этого можно использовать метод dispatch для выполнения соответствующей функции передавая её суффикс, например x.dispatch('first'). Использование специальных классов Если есть желание использовать синтаксис 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 return True elif self.value in args: self.fall = True return True return 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('Другое число') Использование операторов and и or. Довольно небезопасный способ, см. пример: # Условная конструкция 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 вычисляющееся как True, f вычисляется как 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, вместо того, чтобы вернут этот ноль в качестве результата выражения. Использование исключений Если объявить несколько функций: 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 Здесь используется функция sys.exc_info, которая возвращает набор из информации о обрабатываемом исключении: класса, экземпляра и стека. Код с использованием этих конструкций будет выглядеть следующим образом: n = int(input()) try: switch(n) except ( case(1), case(2), case(3) ): print "Число от 1 до 3" except case(4): print "Число 4" except: print "Другое число"

Ответ 2



Самый простой и быстрый способ: from collections import defaultdict selector = defaultdict(lambda: 'неизвестно', {'a': 1, 'b': 'text', 'c': func1, 'd': self.func2}) selector['a'] selector['d']('test')

Комментариев нет:

Отправить комментарий