Практическое применение условной конструкции match case в Python

Практическое применение условной конструкции match case в Python

В Python есть несколько возможностей реализации условных конструкций. В данной статье будет рассмотрена конструкция match case, которая может быть полезна для сопоставления получаемого значения с образцами как альтернатива классическому if else.


Базовый пример использования с простыми типами

После оператора match указывается объект для сопоставления. Затем после оператора case перечисляются возможные образцы. В случае успешного сопоставления значения с образцом выполняется вложенный блок кода, при этом сопоставление с остальными образцами не производится. Специальный символ _ может использоваться для обработки любого значения, которое не соответствует перечисленным ранее образцам.

def http_status_code_message(code: int) -> str:
  match int(str(code)[0]):
    case 1:
      return "1xx: Informational"
    case 2:
      return "2xx: Success"
    case 3:
      return "3xx: Redirection"
    case 4:
      return "4xx: Client Error"
    case 5:
      return "5xx: Server Error"
    case _:
      return "xxx: Invalid status code"

status_code_list = [
    101,
    201,
    301,
    401,
    501,
    601
]

for status_code in status_code_list:
  print(http_status_code_message(status_code))

1xx: Informational
2xx: Success
3xx: Redirection
4xx: Client Error
5xx: Server Error
xxx: Invalid status code


Примеры использования с более сложными типами или классами

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

Итерируемый объект

Специальный символ * может использоваться для распаковки элементов передаваемого итерируемого объекта.

from typing import List

def http_status_code_list_check(statuses: List[int]) -> str:
  match statuses:
    case [200, *_]:
      return "It started pretty well"
    case [*_, 200]:
      return "All in all it ended well"
    case []:
      return "It didn't worked out at all"
    case _:
      return "We don't interested in such cases"

statuses_list = [
    [],
    [500],
    [200, 301, 404],
    [301, 404, 200]
]

for statuses in statuses_list:
  print(f"{statuses}: {http_status_code_list_check(statuses)}")

[]: It didn't worked out at all
[500]: We don't interested in such cases
[200, 301, 404]: It started pretty well
[301, 404, 200]: All in all it ended well

Словарь

Специальный символ ** может использоваться для распаковки элементов передаваемого словаря. Однако его нельзя комбинировать со специальным символом _.

from typing import Dict

def http_status_code_dict_check(statuses: Dict[int, str]) -> str:
  match statuses:
    case {200: ok}:
      return f"{ok} Only got 2xx"
    case {**rest}:
      return f"{rest} All we got"

statuses_dict = [
    {200: "OK", 404: "Error"},
    {301: "Moved Permanently", 404: "Error"},
]

for statuses in statuses_dict:
  print(f"{statuses}: {http_status_code_dict_check(statuses)}")

{200: 'OK', 404: 'Error'}: OK Only got 2xx
{301: 'Moved Permanently', 404: 'Error'}: {301: 'Moved Permanently', 404: 'Error'} All we got

Объект класса

class StatusCode:
  def __init__(self, code, message):
    self.code = code
    self.code_group = int(str(code)[0])
    self.message = message

  def __repr__(self):
    return f"{self.code_group}xx: {self.message}"

def http_status_code_class_check(status: StatusCode) -> str:
  match status.code_group:
    case 1 | 2 | 3 | 4 | 5:
      return f"Correct status code. {status}"
    case _:
      return "Incorrect status code. xxx: Invalid"

status_code_class_list = [
    StatusCode(101, "Informational"),
    StatusCode(201, "Success"),
    StatusCode(301, "Redirection"),
    StatusCode(401, "Client Error"),
    StatusCode(501, "Server Error"),
    StatusCode(601, "Unknown"),
]

for status_code_class in status_code_class_list:
  print(http_status_code_class_check(status_code_class))

Correct status code. 1xx: Informational
Correct status code. 2xx: Success
Correct status code. 3xx: Redirection
Correct status code. 4xx: Client Error
Correct status code. 5xx: Server Error
Incorrect status code. xxx: Invalid


Пример использования в анализе данных

import pandas as pd
import sqlite3


df = pd.read_csv(
"https://raw.githubusercontent.com/" + \
"datasciencedojo/datasets/refs/heads/master/titanic.csv"
)

conn = sqlite3.connect("bdt.db")

query_ddl = """
create table if not exists titanic (
    passenger_id integer,
    survived integer,
    p_class integer,
    name text,
    sex text,
    age real,
    sib_sp integer,
    parch integer,
    ticket text,
    fare real,
    cabin text,
    embarked text
);
"""

conn.execute(query_ddl)
conn.commit()

df.to_sql(
    name="titanic",
    con=conn,
    if_exists="replace",
    index=False
)

cursor = conn.cursor()

cursor.execute("select * from titanic limit 5")

from typing import Tuple

def map_sex(data: Tuple) -> int:
  match data[4]:
    case "male":
      return 0
    case "female":
      return 1
      
data = cursor.fetchone()
while data:
  print(f"{data[4]}, {map_sex(data)}")
  data = cursor.fetchone()

conn.close()

male, 0
female, 1
female, 1
female, 1
male, 0


Заключение

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

Полезные ссылки

Курсы по большим данным, машинному обучению и разработке на Python | BigData Team
Обучаем IT-специалистов и руководителей. Современные инструменты, практические задачи, бизнес-кейсы, наставники из отрасли.