Сообщить об ошибке.

Имитация оператора switch/case в Python

Оператор switch/case - это оператор, который оценивает точность или сходство переданного значения. Почти во всех языках (таких как Java и .NET C# и т.д.), в синтаксисе, присутствует оператор switch/case.

В версии Python 3.10 Гвидо ван Россум анонсировал конструкцию pattern matching (сопоставление с образцом). Саму концепцию сложно назвать новой, она уже реализована во многих языках как switch/case. Синтаксически конструкция pattern matching по сути аналогична тому, как это представлено в ряде других языков.

Начиная с Python 3.10 эта конструкция представлена сочетанием операторов match/case:

match some_expression:
    case pattern_1:
        ...
    case pattern_2:
        ...

Как имитировать поведение switch/case до Python 3.10

Между тем, до версии Python 3.10 прекрасно обходился без конструкции match/case. Случай с оператором match очень похож на оператор if/elif/else. По большому счету это то же самое, что делает оператор if/elif/else.

fruit = "banana"
if fruit == "apple":
    print("The fruit is apple")
elif fruit == "banana":
    print("The fruit is banana")
elif fruit in ("blueberries", "raspberries", "blue currant", "currant"):
    print("The fruit is berries")
else:
    print("The fruit is unknown")

Приведенный выше код просто читать и понимать. Но это все еще не оператор switch/case. switch оценивает точность, но также и сходство слова. В python есть объект, который действует как селектор.

Селектор оценивает выражение, принимает его значение и проверяет точность шаблона, в противном случае он возвращает значение по умолчанию. В Python объект словаря - это своего рода селектор:

Объект сопоставления в Python отображает хешируемые значения на произвольные объекты. Сопоставления - это изменяемые объекты. В настоящее время существует только один стандартный тип сопоставления - dict.

Основываясь на хэш-индексах, он намного быстрее извлекает значения. Попробуем создать селектор.

fruits = {
    "apple": "The fruit is apple",
    "banana": "The fruit is banana",
    "blueberries": "The fruit is berries",
    "raspberries": "The fruit is berries",
    "blue currant": "The fruit is berries",
    "currant": "The fruit is berries"
}

>>> myfruit = "banana"
>>> print(fruits.get(myfruit, "The fruit is unknown"))
# The fruit is banana

Таким образом switch-селектор получился красивым и "питоническим". Но все равно оценивается только точность слова, а сходство слова - нет.

Как можно улучшить код, представленный выше? Создадим функцию, которая будет действовать как селектор (оценивать точность и сходство переданного в нее слова). Что бы добиться такого поведения нужна функция, которая оценивает сходство ключей словаря. Такая функция есть в Python и называется filter().

def fruit_selector(fruit):
    fruits = {
        "apple": "The fruit is apple",
        "banana": "The fruit is banana",
        "blueberries": "The fruit is berries",
        "raspberries": "The fruit is berries",
        "blue currant": "The fruit is berries",
        "currant": "The fruit is berries",
        "default": "The fruit is unknown"
    }
    return dict(filter(lambda key: fruit in key[0], fruits.items()))

>>> fruit_selector('banana')
>>> fruit_selector('anana')

В python методы классов и функций являются объектами типа Callable. Как объекты, в них может храниться информация. Превратим fruit_selector() в switch с дополнительным значением: кеш последнего выбранного фрукта. В Python все является объектом и, следовательно, может рассматриваться как таковое. Если бы было необходимо еще больше абстрагировать селектор, то он фактически стал похож на switch, вот и все:

def switch(match, dictionary, default="no match"):
    for key in dictionary.keys():
        if match in key:
            return dictionary.get(key)
    return default

fruits = {
    "apple": "The fruit is apple",
    "banana": "The fruit is banana",
    "blueberries": "The fruit is berries",
    "raspberries": "The fruit is berries",
    "blue currant": "The fruit is berries",
    "currant": "The fruit is berries"
}

>>> switch('banana', fruits)
# 'The fruit is banana'
>>> switch('anana', fruits)
# 'The fruit is banana'
>>> switch(switch.last_match, fruits, default="The fruit is unknown")
# 'The fruit is banana'

Так как функции в Python являются объектами, то можно заменить значения ключей словаря функциями и возвращать их, если ключи совпадают. Очень простой пример.

def switch(match, dictionary, default="no match"):
    for key in dictionary.keys():
        if match in key:
            return dictionary.get(key)
    return default

calc = {
    "plus": lambda x, y: x + y,
    "minus": lambda x, y: x - y
}

>>> plus = switch('plus', calc, default="unintended function")
>>> minus = switch('minus', calc, default="unintended function")
>>> plus(6, 4)
# 10
>>> minus(6, 4)
# 2