17

Замены для оператора switch в Python?

9

Я хочу написать функцию на Python, которая возвращает разные фиксированные значения в зависимости от значения входного индекса.

В других языках я бы использовал конструкции switch или case, но, похоже, в Python нет оператора switch. Каковы рекомендуемые решения для этой задачи в Python?

5 ответ(ов)

4

В дополнение к методам словарей (которые мне очень нравятся, кстати), вы также можете использовать конструкции if-elif-else, чтобы добиться функциональности switch/case/default:

if x == 'a':
    # Сделать что-то
elif x == 'b':
    # Сделать что-то другое
if x in 'bc':
    # Переход по умолчанию, не используя elif, но теперь значение по умолчанию включает случай 'a'!
elif x in 'xyz':
    # Сделать ещё что-то
else:
    # Выполнить значение по умолчанию

Конечно, это не идентично switch/case — вы не можете легко реализовать fall-through, просто убрав оператор break, но вы можете провести более сложную проверку. Его форматирование выглядит лучше, чем серия вложенных if, хотя по сути это именно то, к чему оно ближе.

1

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

Ваша реализация

Класс switch задает значение через специальный метод __new__, который по сути является конструктором. Тем не менее, использование класса для этой цели может быть излишним и не совсем соответствует естественным принципам ООП в Python.

Функция case принимает произвольное количество аргументов и проверяет, совпадает ли одно из них с значением switch.value. Это делает проверку условий очень лаконичной.

Пример использования

Вы приводите пример использования в цикле while, где сначала происходит установка значения n с помощью вызова switch(n), затем проверяются разные случаи с использованием case. Это позволяет избежать большого количества вложенных if.

Пояснение работы

Пример:

n = 2
switch(n)

if case(0):
    print("You typed zero.")
if case(1, 4, 9):
    print("n is a perfect square.")
if case(2):
    print("n is an even number.")
if case(2, 3, 5, 7):
    print("n is a prime number.")
  1. Если n равно 2, то будет напечатано "n is an even number."
  2. Поскольку 2 также является простым числом, будет напечатано еще и "n is a prime number."

Таким образом, ваш код демонстрирует, как можно удобно расписывать логику обработки различных случаев, но стоит учесть, что только один вывод будет произведен за итерацию. Если нужно, чтобы все условия проверялись, вам придется немного изменить логику.

Тесты

n = 2
# Вывод:
# n is an even number.
# n is a prime number.

n = 11
# Вывод:
# Only single-digit numbers are allowed.

Заключение

Ваша конструкция действительно предоставляет удобный способ деления логики по случаям, но может быть улучшена за счет большей читаемости или альтернативных подходов, таких как использование словарей.

0

Вы можете использовать этот класс Switch для имитации поведения оператора switch в Python. В вашем примере с использованием Switch для определения дня недели создается контейнер, который позволяет удобно обрабатывать различные условия без необходимости писать несколько вложенных if.

Вот как это работает:

  1. Определяется класс Switch, который принимает значение в конструкторе.
  2. Реализованы методы __enter__ и __exit__, что позволяет использовать его в контексте with. Метод __exit__ возвращает False, чтобы раскинуть исключения в случае их возникновения.
  3. Метод __call__ позволяет использовать экземпляр класса как функцию, проверяя, содержится ли значение в переданных аргументах.

Пример использования Switch с текущим днем недели:

from datetime import datetime

with Switch(datetime.today().weekday()) as case:
    if case(0):
        print("Я так ненавижу понедельники.")
    elif case(1, 2):
        print("Когда же наступят выходные?")
    elif case(3, 4):
        print("Выходные близко.")
    else:
        print("Давайте повеселимся!") 

При этом, если сегодня понедельник, программа выведет "Я так ненавижу понедельники." Если день — вторник или среда, вы получите "Когда же наступят выходные?", и так далее.

Такой подход позволяет избежать большого количества if-elif, делает код более читаемым и упрощает добавление новых условий в будущем.

0

В приведенном вами примере кода используется шаблон, который я узнал из кода Twisted Python. Этот подход позволяет динамически вызывать методы на основе команд, которые вы получаете, что полезно для реализации различных протоколов или автоматов состояний.

Вот основной код:

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'

С помощью метода lookupMethod вы можете изящно выбрать и вызвать соответствующий метод, начинающийся с префикса do_, на основе полученной команды (например, HELO или QUIT). Это позволяет легко расширять функциональность, добавляя новые методы в классе-наследнике, без необходимости изменять существующий код.

Такой подход хорошо работает в контексте автоматов состояний. Например, в вашей реализации состояний вы можете иметь методы, начинающиеся с state_, и запускать вызов метода в зависимости от текущего состояния. Основной класс может не содержать методов do_, а ваши специфичные реализации могут их добавлять.

Вот пример использования в контексте обработки команд:

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

Когда вы получаете строку команды, такую как ' HELO foo.bar.com ', она разбивается на части, и метод для обработки команды (do_HELO) вызывается на основе первой части. Если команда неизвестна, будет вызван метод do_UNKNOWN, который выбросит исключение.

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

0

Вы можете использовать словарь для вызова функций в зависимости от значения переменной. Это позволяет более элегантно управлять выполнением различных функций без повторного написания условных операторов. Вот как это можно организовать на Python:

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

В этом примере foo1(), foo2() и foo3() являются функциями, которые будут вызваны в зависимости от значения переменной option.

Пример 1 (с параметрами):

option = number['type']
result = {
    'number':     value_of_int,  # результат будет value_of_int(number['value'])
    'text':       value_of_text,  # результат будет value_of_text(number['value'])
    'binary':     value_of_bin,   # результат будет value_of_bin(number['value'])
}.get(option)(number['value'])

Здесь в зависимости от типа будет вызвана соответствующая функция и передано значение number['value'] в качестве аргумента.

Пример 2 (без параметров):

option = number['type']
result = {
    'number':     func_for_number, # результат будет func_for_number()
    'text':       func_for_text,   # результат будет func_for_text()
    'binary':     func_for_bin,     # результат будет func_for_bin()
}.get(option)()

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

Пример 4 (только значения):

option = number['type']
result = {
    'number':    lambda: 10,       # результат будет 10
    'text':      lambda: 'ten',    # результат будет 'ten'
    'binary':    lambda: 0b101111, # результат будет 47
}.get(option)()

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

Этот метод позволяет избежать сложной логики с условными операторами и сделать код более чистым и поддерживаемым.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь