[Перевод] Матчинг шаблона в Python 3.10

b789be6ef46953bc85e49d4f206ec8a2.png

В Python 3.10 имплементирован своего рода оператор switch — что-то вроде него. Оператор switch в других языках, таких как C или Java, выполняет простой матчинг значения переменной и исполняет код в зависимости от этой величины.

Он может использоваться просто, как обычный оператор switch, но способен на гораздо большее.

Возможно, этого было бы достаточно для языка C, но ведь речь о Python, а в Python 3.10 реализована гораздо более мощная и гибкая конструкция, называемая структурным сопоставлением шаблона. Она может использоваться как простой оператор switch, но способна на гораздо большее.

Давайте рассмотрим несложный пример с оператором switch. Ниже приведен сниппет, который осуществляет выбор одного значения. Мы проверим его, запустив в цикле со значениями 1,2,3 и 4.

for thing in [1,2,3,4]:
    match thing:
        case 1:
            print("thing is 1")
        case 2:
            print("thing is 2")
        case 3:
            print("thing is 3")
        case _:
            print("thing is not 1, 2 or 3")

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

Это не отличается от оператора switch/case в других языках, но в отличие, например, от языка C, после исполнения кода для конкретного кейса проверка переходит к завершению оператора match.

Если совпадения нет, то происходит выполнение кода в случае по умолчанию, обозначенном символом -.

И вот результат.

thing is 1
thing is 2
thing is 3
thing is not 1, 2 or 3

В этом нет ничего удивительного.

Оператор switch — это простой кейс матчинга шаблона, но Python идет немного дальше. Взгляните на этот код:

for thing in [[1,2],[9,10],[1,2,3],[1],[0,0,0,0,0]]:
    match thing:
        case [x]:
            print(f"single value: {x}")
        case [x,y]:
            print(f"two values: {x} and {y}")
        case [x,y,z]:
            print(f"three values: {x}, {y} and {z}")       
        case _:
            print("too many values")

Это снова оператор match в цикле, но на этот раз в качестве перечня значений, которые цикл будет итерировать, выступают сами списки — сначала [1,2] затем [9,10] и так далее.

Операторы case пытаются выполнить матчинг этих списков. Первый case соответствует списку с одним элементом, второй — списку с двумя элементами, третий — списку с тремя элементами. Последний case используется по умолчанию.

Но он делает больше, чем это. Он также выполняет биндинг значений, которые сопоставляются с идентификаторами в операторе case. Так, например, первый список — это [1,2], и он соответствует второму случаю [x,y]. Таким образом, в выполняемом коде идентификаторы x и y принимают значения 1 и 2, соответственно. Совсем неплохо, правда?

Итак, результат таков:

two values: 1 and 2
two values: 9 and 10
three values: 1, 2 and 3
single value: 1
too many values

Из нашей первой несложной программы мы знаем, что можно выполнять матчинг значений, а из приведенной выше — матчинг других, более общих шаблонов. Итак, можем ли мы делать матчинг шаблонов, включающих значения? Конечно!

for thing in [[1,2],[9,10],[3,4],[1,2,3],[1],[0,0,0,0,0]]:
    match thing:
        case [x]:
            print(f"single value: {x}")
        case [1,y]:
            print(f"two values: 1 and {y}")
        case [x,10]:
            print(f"two values: {x} and 10")
        case [x,y]:
            print(f"two values: {x} and {y}")
        case [x,y,z]:
            print(f"three values: {x}, {y} and {z}")       
        case _:
            print("too many values")

Например, во втором кейсе мы осуществляем матчинг списка, состоящего из двух элементов, первый из которых имеет значение 1.

Вот результат:

two values: 1 and 2
two values: 9 and 10
two values: 3 and 4
three values: 1, 2 and 3
single value: 1
too many values

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

Вот программа, которая будет выполнять матчинг списка с любым количеством элементов.

for thing in [[1,2,3,4],['a','b','c'],"this won't be matched"]:
    match thing:
        case [*y]:
            for i in y:
                print(i)
        case _:
            print("unknown")

В первом case индентификатор отмечен звездочкой, и это означает, что к нему можно привязать весь список, который, как мы видим, можно итерировать с помощью цикла.

1
2
3
4
a
b
c
unknown

Это еще не все, но, надеюсь, данная статья дала вам представление о структурном сопоставлении шаблона в Python 3.10.

Более расширенно о дополнительном способе матчинга шаблона я писал здесь.

На сайте Python.org есть полное описание структурного сопоставления шаблона, включая учебное руководство, в PEP 636. Учебник довольно хороший, и я советую вам его прочитать.

В Python 3.10 довольно много новых разработок, помимо структурного матчинга шаблона. О них рассказывается в нескольких статьях, например, см. Джеймс Бриггс Новые возможности в Python 3.10.

Однако имейте в виду, что 3.10 еще только бета-версия, и хотя примеры, которые я показал выше, работают отлично, вам не следует использовать Python 3.10 со всей серьезностью до его официального релиза.

Приглашаем всех заинтересованных на открытое занятие »Знакомство с веб разработкой на Flask». На нем познакомимся с основами веб разработки на Flask, а также научимся создавать и рендерить шаблоны страниц. Попробуем создать Flask приложение, затем создать роуты и в конце обработать различные HTTP методы на Flask. Регистрирация — по ссылке.

© Habrahabr.ru