Это больше, чем просто выключатель

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

Чтобы идти в ногу с развитием других развивающихся языков, таких как Go, Julia и Rust, Python Software Foundation вкладывает значительные ресурсы в постоянное улучшение Python.

Недавно было выпущено крупное обновление версии Python 3.10. Не дайте себя обмануть, что это последний крупный выпуск Python 3, поскольку сейчас рассматривается 3.11, как указано на официальном сайте Python.

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

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

Для удобства я сделаю смелый шаг и назову сопоставление структурных шаблонов SPM, что является аббревиатурой этого метода. Обратите внимание, насколько мне известно, я не встречал такого имени в официальной документации. Мне легко писать.

Примечание: Python 3.10 был официально выпущен, и вы можете найти его на официальном сайте Python. Вот ссылка на страницу загрузки.

Обзор и основной синтаксис

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

Предположим, что мы получаем ответ от вызова API, который возобновляет объект кортежа с первым элементом, являющимся кодом состояния, а второй элемент - данными, в зависимости от того, был ли запрос успешным или нет.

Другими словами, когда запрос не выполнен, кортеж содержит только один элемент. Ниже показано возможное решение:

Как видите, решение соответствует нашим бизнес-требованиям. Однако по мере развития наших требований нам придется создавать дополнительные ветви с несколькими уровнями вложенных операторов if…elif…else…. Когда это происходит, читаемость и, следовательно, ремонтопригодность нашей кодовой базы значительно снижается.

Именно здесь вступает в игру SPM, который разработан для элегантного управления этим бизнесом. Эта новая функция, добавленная в 3.10, включает использование двух новых ключевых слов: match и case (строго говоря, они известны как мягкие ключевые слова, см. Обсуждение здесь).

Давайте посмотрим, как мы можем использовать SPM для реализации этой потребности. Возможное решение показано ниже:

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

Соответствовать нескольким значениям

В предыдущем фрагменте кода вы видели, что мы используем несколько предложений case для создания нескольких шаблонов, по которым resp можно проверить.

Однако SPM гибок в том, что каждый шаблон может содержать несколько значений вместо одного. В следующем фрагменте кода показан возможный вариант использования.

match resp:
    case (200, data) | (100, data):
        ...
    case ((404 | 401), data):
        ...
    case ((404 | 401) as status_code, data):
        ...
  • Вы можете объединить различные шаблоны в одно предложение case. Используя это, вы просто проверяете несколько значений. Эти альтернативные шаблоны или значения разделяются оператором объединения (вертикальная черта).
  • В качестве альтернативы в каждом шаблоне вы можете указать возможные значения с помощью операции объединения.
  • Чтобы добавить дополнительную гибкость, мы можем привести возможные значения к переменной: (404 | 401) as status_code, чтобы мы могли использовать status_code на более позднем этапе.

В более общем смысле переменные, отображаемые в шаблонах, могут называться захватами. По сути, значение, которое соответствует шаблону, присваивается соответствующим переменным в шаблоне, так что эти захваченные значения могут использоваться для дальнейшей обработки.

Применить условия

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

special_codes = [300, 301, 302]
match resp:
    case (code, data) if code in special_codes:
        ...

Чтобы применить условие, вы просто добавляете оператор if после шаблона. Обратите внимание, что переменные, используемые в шаблоне, сразу же доступны в операторе if.

Помеченное выражение и подстановочный знак

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

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

>>> def spm_starred(resp):
...     match resp:
...         case (code, *items):
...             print("items:", items)
... 
...             
>>> 
>>> spm_starred((200, "item1", "item2", "item3"))
items: ['item1', 'item2', 'item3']

Еще одна полезная функция, которую мы использовали без упоминания, - это шаблон подстановки. В частности, мы используем подчеркивание для обозначения всего, что не соответствовало предыдущим шаблонам. Это похоже на предложение default в операторе switch-case на других языках.

match something
    case pattern0:
        ...
    case pattern1:
        ...
    case pattern2:
        ...
    case _:
        # this is the wildcard pattern

Сопоставьте сложные объекты

Шаблоны, которые мы использовали, довольно просты: строки, целые числа и кортежи. Сила SPM заключается в том, что он может сопоставлять сложные шаблоны, которые включают в себя, казалось бы, сложные объекты, такие как словари и пользовательские экземпляры.

match resp_dict:
    case {"code": status_code, "data": data}:
        print("Status Code:", status_code)
        print("Data:", data)

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

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

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

Заключение

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

Когда вам нужно иметь дело с несколькими возможными сценариями, возможно, это лучший момент для использования структурного сопоставления с образцом, которое имеет гораздо лучшую читаемость, чем несколько операторов if…elif…else.