Главный секрет операторов match/case в пайтоне
Не так давно увидела свет версия языка пайтон 3.10. В ней был добавлен pattern matching statement (оператор сопоставления с шаблонами). Как гласит официальное описание этого оператора в PEP622, разработчики в большей мере вдохновлялись наработками таких языков как: Scala, Erlang, Rust.
Многие, в том числе и я, встретили оператор с критикой. Можно для примера почитать комментарии к недавнему посту. В основном люди жалуются на синтаксис, который похож на синтаксис пайтона, однако означает совершенно другое. Вот несколько примеров:
match values:
case name, "1"|"2" as access:
print(f"Access for {name} granted with {access}")
case _:
print("Deny")
Здесь выражение "1"|"2" as access
очень похоже на то, что мы уже много раз видели в пайтоне, например в with
или except
. Слева — обычное выражение на пайтоне, справа название переменной, которой присвоится это выражение. Но присмотритесь внимательнее, "1"|"2"
это бессмыслица, так как |
— это оператор бинарного ИЛИ, которое очевидно не может существовать для строк. Здесь оператор |
— часть механизма pattern matching, а не языка пайтон.
Ещё хуже придется, если флаги, который вы хотите проверить, как раз будут битовыми:
match values:
case name, 0b001 | 0b010 as access:
print(f"Access for {name} granted with {access}")
case _:
print("Deny")
0b001 | 0b010
должно означать «права на то и на другое» (например, на чтение и на запись), однако в pattern matching это не является выражением пайтона и пользователь получает доступ имея права только на что-то одно.
Другое ограничение match/case — вы не можете использовать никакие внешние переменные для сопоставления. Попробуйте вспомнить другое место в пайтоне, где вы можете написать "Vadim"
, но не можете get_username()
или использовать локальную переменную. Я не вспомнил.
class User:
__match_args__ = ('name', 'access')
def __init__(self, name, access):
self.name = name
self.access = access
def match_name(data_class, username):
match data_class:
case User(username) as req:
print(f"Granted to {req.name}")
case _:
print("deny")
match_name(User("Anna", 1), "Vadim")
Granted to Anna
Кстати, заметили выражение UserRequest(username)
? Выглядит как создание экземпляра класса, однако очевидно им не является, хотя бы потому, что у класса два обязательных аргумента, а тут передано только одно значение.
Из всего изложенного можно сделать вывод:
Операторы match/case не являются частью языка пайтон, это встроенный язык со своим уникальным синтаксисом!
Можно представить, что создатели решили бы встроить не pattern matching, а например SQL.
users = [
User("Anna", 0x011),
User("Vadim", 0x010)
]
granted = SELECT users.name FROM users WHERE users.access IN (1, 2);
Вряд ли в этом случае можно было бы ожидать, что внутри такого выражения стало бы работать всё богатство синтаксиса пайтона.
Таким образом, никакой синтаксис и выражения из пайтона и не должны работать внутри match/case по умолчанию. Другой вопрос, насколько оправданно было встраивать в пайтон другой язык, с таким похожим синтаксисом и совершенно другой семантикой.