match-case: сопоставление структурных шаблонов
Условные конструкции есть практически в каждом языке программирования, и Python - не исключение. Они позволяют выполнять различные действия, в зависимости от того, истинно ли определенное условие. Python долгое время был ограничен стандартными операторами if
, elif
и else
, но среди разработчиков давно возник запрос на операторы сопоставления с шаблонами (англ. pattern matching statement
). Обычные условные блоки могут разрастаться до монструозных конструкций, когда нам всего-то надо проверить какую-то переменную на несколько условий. Такой шаблонный код является плохой практикой, и во многих современных языках существуют операторы switch
и case
, или их аналоги, сильно облегчающие работу программиста. Начиная с версии 3.10 в Python появились свои операторы match
и case
, вдохновленные аналогами из языков Scala и Erlang.
Синтаксис
Конструкция должна состоять из инструкции match
, за которым следует субъект (значение), и нескольких условий case
. За каждым case
следует паттерн, с которым будет сравниваться субъект. Опционально, за паттерном может следовать "страж" — условие, которое проверяется, если шаблон подошел. Далее следует блок кода, исполняемый при выполнении условий.
Паттерном называется новая синтаксическая конструкция. Выглядит она точно так же, как создание объекта, но делает абсолютно противоположное — извлекает компоненты субъекта по определенному шаблону. Использование паттернов позволяет упростить код, особенно если нам нужно проверять типы данных.
Давайте перепишем функцию create_dot
, создающую двухмерную точку класса Dot
по двум координатам, взятым из кортежа или объекта класса Point
.
def create_dot(value):
if isinstance(value, tuple) and len(value) >= 2:
return Dot(value[0], value[1]), value[2:]
elif isinstance(value, Point) and value.z == 0:
return Dot(value.x, value.y)
else:
return TypeError(
"Невозможно создать точку"
)
В старой версии мы видим привычную конструкцию if-elif-else
. Как видите, нам приходится проверять сразу несколько утверждений и объединять их логическими операторами. Конструкция match-case
будет гораздо лаконичнее.
def create_dot(value):
match value:
case (x, y, *rest):
return Dot(x, y), rest
case Point(x=x, y=y, z=0):
return Dot(x, y)
case _:
return TypeError(
"Невозможно создать точку"
)
dot, rest = create_dot((1, 2, 3, 4))
print(dot.x, dot.y, rest)
# 1 2 [3, 4]
dot = create_dot(Point(1, 2, 0))
print(dot.x, dot.y)
# 1 2
print(create_dot('0, 1'))
# Невозможно создать точку
С увеличением количества необходимых проверок, match-case
становится все более предпочтительным вариантом.
Паттерны
Для разных целей можно использовать разные шаблоны. Буквальный паттерн (англ. literal) - самый простой из них. Это число, строка, булево значение или None
match value:
case 0:
...
case "zero":
...
case True:
...
Паттерн захвата (англ. capture) позволяет захватить переменную. Захваченную переменную можно использовать внутри соответствующего блока.
match value
case x:
print(f'The x is {x}')
case [a, b]:
print(a + b)
Подстановочный паттерн (англ. wildcard) обозначается нижним подчеркиванием - _
. Он подходит к любому субъекту, но не может захватить какие-то переменные субъекта. Его часто используют как замену else
.
Паттерны классов используются для доступа к аттрибутам класса.
match frame
case LeftFrame(size, color="orange"):
...
Паттерны можно комбинировать при помощи вертикальной черты |
, которая эквивалентна or
.
match n
case (0 | 1):
...
В паттернах может быть удобно использовать моржовый оператор:
match line:
case Line(start := Point(x, y), end) if start == end:
print("Точка с координатами {x}, {y}")
Страж
Страж (англ. guard) - условное выражение, позволяющее выполнить блок кода только если оно возвращает True.
match value
case [x, *rest] if x == 0:
...
case [x, *rest] if x > 0:
...
case _:
...
Стражи не допустимы во вложенных паттернах. Выражения типа [x if x == 0]
будут вызывать SyntaxError
.
Заключение
Сопоставление структурных шаблонов может сильно упростить проверки утверждений. Более лаконичный синтаксис делает чтение блоков match-case
более интуитивным, чем if-elif-else
. Паттерны не только упрощают код, но и позволяют более гибко использовать субъекты. Эта конструкция особенно полезна, если субъекты могут принадлежать к разным типам.
Возможно будет интересно
Как практиковаться в Python?
Для улучшения качества знаний и повышения уровня программиста, необходим постоянный практикум. Где можно это организовать самостоятельно, и как практиковаться в Python?
Функторы в Python
Функциональные объекты, или функторы в программировании — объекты, которые можно вызывать, подобно функциям. В этой статье мы разберемся, как создавать функторы и какие возможности они открывают.
Шпаргалка по модулю itertools
Шпаргалка по всем функциям модуля itertools, создающим разнообразные итераторы.