7

Я просматривал ответ на вопрос, а потом узнал, что match не такой же, как обычный switch.

Пример:

a = 2
b = 2

match a:
    case 1: print("1!")
    case b: print("2!") #<-- так нельзя.
    case 3: print("3!")

А так же там вез��е советовали использовать "точечную запись" для решения этой проблемы. Даже показали как оно там устроено.

Окей, это конечно же всё круто, только похоже на обычное "низкоуровневое" объяснение, типа "вот так сделай и не парь себе мозги".

Но где я могу получить более высокоуровневое объяснение?

В чём концептуальная идея невозможности использования констант для match в python?

2
  • case b: определяет новую переменную, равную значению выражения из match . Так сделано. Commented 3 дек. 2023 в 19:58
  • 3
    хрень какую-то придумали) сделали бы 2 отдельных оператора "switch" и "match"
    – eri
    Commented 3 дек. 2023 в 20:03

2 ответа 2

11

match реализует не сравнение, а т.н. Сопоставление с образцом - очень распространенный прием в функциональном программировании.

Значение в match сопоставляется с одним или несколькими образцами, состоящими из литералов, имен классов и переменных. Тело первого case с совпадающим образцом выполняется, остальные игнорируются. При этом переменным из образца присваиваются значения, которым они соответствуют.

lst = [1, 2, 3, 4, 5, 6]

match lst:
    case [x, _, y, _, *rest]:
        print(x, y, rest) # 1 3 [5, 6]

Переменные x, y, rest создаются в этом месте. Сравнения с ними не производится.

В некотором смысле эта конструкция похожа на обычное множественное присваивание

[x, _, y, _, *rest] = lst
print(x, y, rest) # 1 3 [5, 6]

За некоторыми исключениями:

  • В случае несоответствия выражения образцу не произойдет ошибки
match [1]:
    case x, y, z: # эта ветка проигнорируется
        pass

x, y, z = [1] # ошибка
  • Паттерны вроде (x,y,z), [x,y,z], x,y,z (это синонимы), как в примере выше, нельзя сопоставлять произвольным итерируемым объектам, только последовательностям (реализующим Sequence), кроме строк (и bytes).
match 'test':
    case x, y, *z: # здесь не будет совпадения
        print(x, y, z)

match map(int, '1234'):
    case x, y, *z: # здесь тоже
        print(x, y, z)

x, y, *z = 'test'
print(x, y, z) # t e ['s', 't']

x, y, *z = map(int, '1234')
print(x, y, z) # 1 2 [3, 4]
  • _ является частью синтаксиса, а не именем переменной, и эта часть выражения по настоящему игнорируется, ни чему не присваивается, и время на ее копирование не тратится.
match range(1000000):
    case x, *_, y: # здесь время потратится только на извлечение двух элементов
        print(x, y) # 0 999999
        # попытка вывести _ приведет к ошибке отсутствия соответствующей переменной

x, *_, y = range(1000000) # здесь - также на копирование последовательности в список `_`
print(_) # [1..99998]

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

match a:
    case b:
        # здесь b при сваеивается значение a

это то же самое, что

b = a

Но если вам действительно нужно сравнение

a = 2
b = 2

match a:
    case 1: print("1!")
    case x if x == b: print("2!") #<-- так можно.
    case 3: print("3!")

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

from dataclasses import dataclass

@dataclass
class Data:
    x: bool
    y: str

for rec in [Data(True, 'a'), Data(True, 'B'), Data(False, 'b')]:
    match rec:
        case Data(True, y) if y.islower():
            print(y)
        case _:
            print(rec, "не совпадает")
a
Data(x=True, y='B') не совпадает
Data(x=False, y='b') не совпадает
4

как всегда в питоне немного перемудрили. один и тот же матч делает и сравнение и присваивание.

присваивание он делает, если положить в match список, а в case подбирается количество переменных для распаковки.

в вашем случае не понятно присваивание должно делаться или сравнение.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками или задайте свой вопрос.