Python Switch operator

July 07, 2020

Полный гайд по реализации switch-case в питоне

В Python нет привычного оператора Switch как в других языках и после Java, JavaScript или PHP переходя на Python бывает непривычно писать логику без switch-case инструкций. Даже в Bash есть case оператор. Но выход есть и не один! Рассмотрим все варианты реализации логики switch-case на Python.

А еще этот вопрос встречал на собеседованиях. Так что велком под кат, будем разбираться...

Вариант 1: Словари

Самый простой и распространенный вариант - это использовать словари в качестве switch конструкции:


x = 'bar'

switch = { ‘foo’: 123, ‘bar’: 456, ‘buz’: 789, }[x]

print(switch)

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


let x = ‘bar’

let switch = { ‘foo’: 123, ‘bar’: 456, ‘buz’: 789, }[x] ?? null

console.log(switch)

Заметили разницу? Её почти нет. Обратили внимание что мы использовали Nullish Coalescing оператор? Это на случай если обратимся к несуществующему ключу. А в Python как быть?

Да, в Python есть что-то похожее:


x = ‘wtf’

switch = { ‘foo’: 123, ‘bar’: 456, ‘buz’: 789, }[x] or Null

Traceback (most recent call last):

File “switch.py”, line 3, in <module>

switch = {

KeyError: ‘wtf’

Но, к сожалению, в таком варианте условие не успеет отработать и будет ошибка. Но у словаря уже есть метод get который вернет None в случае отсутствия ключа:


x = ‘wtf’

switch = { ‘foo’: 123, ‘bar’: 456, ‘buz’: 789, }.get(x)

Мы так же можем использовать условия и писать такие конструкции:


def get_size_text(x: int):
return {
x < 10: ‘Small’,
10 <= x < 20: ‘Medium’,
20 <= x: ‘Big’
}[True]

print(get_size_text(13))

Но это еще не все.

Классы

Тут уже на что хватит фантазии, но по сути все варианты сводятся к следующим нескольким идеям.

Идея с цепочками (chaining)

Мы можем написать такой простенький класс:

class Switch:
def init(self, val):
self.val = val

def case(self, val, f):
    if self.val == val:
        f()
    return self

def switch_foo(): print(‘Foo’)

def switch_bar(): print(‘Bar’)

def switch_buz(): print(‘Buz’)

x = ‘bar’

Switch(x)
.case(‘foo’, switch_foo)
.case(‘bar’, switch_bar)
.case(‘buz’, switch_buz)

Громоздко, конечно, но можно делать более сложную логику.

Другой вариант:


class switch(object):
def init(self, val):
self.emp = False
self.val = val

def __iter__(self):
    yield self.match
    raise StopIteration

def match(self, *args):
    if self.emp or not args:
        return True
    elif self.val in args:
        self.emp = True
        return True
    return False

x = 2

for case in switch(x): if case(1): print(‘Foo’) if case(2): print(‘Bar’) break if case(3): print(‘Buz’) # default if case(): print(‘Default’)

Честно говоря уже too much. Но если хочется чего-то эдакого…

Тернарный оператор

Конечно привычного тернарного оператора в Python так же нет, но есть возможность писать короткие условные конструкции. Так что мы могли бы написать такой блок, выполняющий роль switch-case:


x = 5

switch = ( (1 == x and ‘Foo’) or (2 == x and ‘Bar’) or (3 == x and ‘Buz’) or None )

print(switch)

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

Кстати, в PHP можно использовать подобный подход:

<?php

$x = 2;

( (1 == $x and $switch = ‘Foo’) or (2 == $x and $switch = ‘Bar’) or (3 == $x and $switch = ‘Buz’) or ($switch = null ) );

print($switch);

Но это так, оффтоп, да и нецелесообразно. Но ведь можем? Могём!

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

Ну и в конце-то концов, никто не отменял просто цепочку if-elif-else . Такая конструкция может оказаться сильно выгоднее как по скорости, так и по читаемости.

Материалы по теме

https://docs.python.org/3/faq/design.html#why-isn-t-there-a-switch-or-case-statement-in-python



Profile picture

Written by Alexander Mayorov
Full Stack CTO

© 2022