Почему нельзя использовать константы для match?

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

Пример:

a = 2
b = 2

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

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

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

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

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


Ответы (2 шт):

Автор решения: eri

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

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

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

→ Ссылка
Автор решения: extrn

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') не совпадает
→ Ссылка