Match case в Python.

Match case в Python — полный гайд

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

В Python 3.10 ситуация изменилась — в язык добавили конструкцию match case. Она не только заменяет if-elif, но и умеет распознавать структуру данных — списки, словари, объекты.

Match case работает в Python 3.10 и выше

Как работает match case в Python

Конструкция match case работает по принципу сопоставления с образцом (pattern matching). Она проверяет шаблоны по порядку и срабатывает на первом совпадении — остальные case уже не выполняются.

match expression: case pattern_1: # блок кода 1 case pattern_2: # блок кода 2 ...

Если expression подходит под шаблон pattern_1, выполняется блок кода 1. Если подходит под pattern_2 — выполняется блок кода 2.

Python проверяет шаблоны сверху вниз и выполняет только первый подходящий блок.

Рассмотрим простой пример:

item = "кирка" match item: case "меч": print("⚔️ Ты готов к битве!") case "кирка": print("⛏️ Можно копать алмазы!") case "еда": print("🍗 Восстанавливаем здоровье.") > ⛏️ Можно копать алмазы!

В этом примере переменная item содержит строку "кирка", и этот вариант совпадает с case "кирка", поэтому выполняется соответствующий блок кода.

Иногда ни один шаблон не срабатывает. Для таких случаев используется case _ — он выполняется по умолчанию, если не подошёл ни один из предыдущих вариантов. Это как else в управляющей конструкции if else.

item = "топор" match item: case "меч": print("⚔️ Ты готов к битве!") case "кирка": print("⛏️ Можно копать алмазы!") case "еда": print("🍗 Восстанавливаем здоровье.") case _: print("🤷 Не знаю, что с этим делать...") > 🤷 Не знаю, что с этим делать...

Здесь переменная item содержит "топор", а такого варианта нет среди case, поэтому по умолчанию выполняется код в блоке case _.

В отличие от switch case в JavaScript, C или Java, конструкция match case в Python не "проваливается" через несколько блоков. Как только найден первый подходящий case, Python выполняет только его и выходит. Никакие break не нужны — всё работает автоматически.

Реализация конструкции match case написана на языке C — как и большинство внутренних механизмов CPython. Исходный код можно найти в официальном репозитории — github.com/python/cpython/blob/main/Python/ceval.c.

Фрагмент исходного кода ceval.c, где реализована логика match case (структурного сопоставления с образцом) на языке C.

Официальная документация по match case в трёх ключевых документах:

  • PEP 634 — семантика сопоставления с образцом.
  • PEP 635 — мотивация и дизайн.
  • PEP 636 — учебник с примерами.

Шаблоны в case

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

Основные шаблоны, которые можно использовать в case:

  • Literal pattern — точное совпадение. Используется, когда нужно проверить конкретное значение.
  • Wildcard pattern — по умолчанию. Срабатывает, если ни один другой шаблон не подошёл.
  • Sequence pattern — структура списка или кортежа. Используется для сопоставления с фиксированной структурой (порядок и количество элементов).
  • Mapping pattern — структура словаря. Применяется, когда нужно проверить наличие нужных ключей и их значений.
  • Capture pattern — захват значений. Позволяет сохранить значение в переменную.
  • Class pattern — объекты и классы. Используется для сопоставления значений внутри объектов по их полям.
# Literal pattern — точное совпадение case 42: case "admin": case True: case None: # Wildcard pattern — по умолчанию case _: case _ as value: # Sequence pattern — структура списка или кортежа case [1, 2]: case (a, b): # Mapping pattern — структура словаря case {"key": value}: case {"status": "ok", "data": content}: # Capture pattern — захват значений case x: case [code, message]: # Class pattern — объекты и классы case Point(x, y): case Color(r=255, g=0, b=0):

Рассмотрим каждый из них — с короткими примерами и пояснениями.

Literal pattern — точное совпадение

Самый простой тип шаблонов. Python просто сравнивает значение с указанным числом, строкой, True, False или None. Используется, когда нужно проверить, равно ли выражение какому-то конкретному значению.

Пример с числами:

score = 100 match score: case 0: print("Не повезло") case 100: print("Максимум баллов!") > Максимум баллов!

Пример со строками:

role = "admin" match role: case "user": print("Обычный пользователь") case "admin": print("Администратор") > Администратор

Пример с булевыми значениями:

is_logged_in = True match is_logged_in: case True: print("Вход выполнен") case False: print("Доступ запрещён") > Вход выполнен

Пример с None:

token = None match token: case None: print("Токен не найден") case _: print("Авторизация пройдена") > Токен не найден

⚠️ Важно: Python сравнивает литералы точно. 100 — это число, "100" — строка. Они выглядят похоже, но для match case — это совершенно разные вещи.

Wildcard pattern — по умолчанию

Слово "wildcard" переводится с английского как "подстановка" — что-то, что подходит вместо чего угодно.

Если вы хотите обработать любой случай, который не попал под предыдущие шаблоны — используйте подстановку (case _). Символ подчёркивания _ означает:

Если ни один из case не подошёл — выполнится этот блок.

Пример:

command = "что-то неизвестное" match command: case "start": print("Запуск") case "stop": print("Остановка") case _: print("Команда не распознана") > Команда не распознана

В примере command — не "start" и не "stop", поэтому срабатывает case _ (как else в конструкции if else).

Если вам нужно не просто обработать "всё остальное", но ещё и сохранить значение — используйте case _ as:

command = "пауза" match command: case "старт": log("Запуск процесса") case "стоп": log("Остановка процесса") case _ as unknown: log(f"Неизвестная команда: {unknown}") raise ValueError(f"Команда не распознана: {unknown}")

В примере мы сохраняем неизвестную команду в переменную и используем её сразу в двух действиях — логировании и исключении. Такой подход позволяет обрабатывать и использовать значение прямо внутри блока match, без обращения к переменной command снаружи. Это особенно удобно в более сложных конструкциях.

Sequence pattern — структура списка или кортежа

Шаблоны последовательностей позволяют сравнивать списки, кортежи и другие похожие структуры по форме и содержимому. Вы можете указать, сколько должно быть элементов и какие именно — match case сам сопоставит шаблон с переданными данными.

Пример #1 — список пользователей:

request = ["users", 42] match request: case ["users", 42]: print("Показываем пользователя с ID 42") case ["users"]: print("Показываем список всех пользователей") case _: print("Неизвестная команда") > Показываем пользователя с ID 42

В этом примере мы распознаём структуру и выполняем разную логику.

⚠️ Важно: шаблон сработает только если порядок элементов совпадает с шаблоном. Это одна из ключевых особенностей sequence pattern.

data = ["admin", "login"] match data: case ["login", "admin"]: print("Пользователь логинится как админ") case _: print("Похоже, что порядок перепутан") > Похоже, что порядок перепутан

Mapping pattern — структура словаря

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

Пример:

user = {"role": "admin", "active": True} match user: case {"role": "admin", "active": True}: print("Администратор активен") case {"role": "admin"}: print("Администратор неактивен") case {"role": role}: print(f"Пользователь с ролью: {role}") > Администратор активен

В этом примере мы проверяем ключи и значения в словаре, а match сам разбирает структуру и сохраняет значения в переменные.

⚠️ Важно: шаблоны по словарям в match case работают нестрого. Если указанные ключи есть и совпадают по значению — остальные поля не мешают.

user = {"role": "admin", "active": False} match user: case {"role": "admin", "active": True}: print("Администратор активен") case {"role": "admin"}: print("Администратор неактивен") case {"role": role}: print(f"Пользователь с ролью: {role}") > Администратор неактивен

В этом пример словарь {"role": "admin", "active": False} подходит под шаблон {"role": "admin"}.

✨ Основные особенности шаблона Mapping pattern:

  • Проверяются только указанные ключи.
  • Дополнительные ключи в словаре не мешают.
  • Порядок ключей не имеет значения.
  • Значения можно извлекать.
  • Ключи в шаблоне должны быть литералами.

Capture pattern — захват значений

Иногда важно не просто проверить значение, а сохранить его в переменную, чтобы потом с ним что-то сделать — такой шаблон называется захват значения (capture). Он ловит значение и сохраняет его в переменную, если оно совпадает со структурой шаблона.

Пример #1:

command = "рассылка" match command: case "старт": print("Запуск системы") case "стоп": print("Остановка") case other: print(f"Команда '{other}' не распознана") > Команда 'рассылка' не распознана

В этом примере мы проверяем допустимые команды ("старт" и "стоп"), а всё остальное автоматически попадает в other — это удобно, чтобы сразу вывести название неизвестной команды.

Пример #2 — данные разной структуры:

data = [42] # или [200, "OK"] match data: case [code, message]: print(f"Всё ок: {code} — {message}") case [code]: print(f"Только код: {code}") case _: print("Неизвестный формат") > Только код: 42

Тут мы передаём в match список data. Python по очереди сравнивает его с шаблонами. В обычном if-else пришлось бы сначала проверять len(data), потом вручную распаковывать список и следить за индексами.

Пример #3 — словари с нужной структурой:

response = {"status": "ok", "data": [1, 2, 3]} match response: case {"status": "ok", "data": content}: print(f"Получили данные: {content}") case {"status": "error", "message": msg}: print(f"Ошибка: {msg}") case _: print("Что-то непонятное") > Получили данные: [1, 2, 3]

В этом примере мы проверяем структуру словаря и сразу вытаскиваем нужные данные, если поля совпадают с ожидаемыми. Через if-else это выглядело бы как вложенные проверки ключей и типов — много кода, высокая вероятность ошибиться.

⚠️ Важно: переменные из case попадают в текущую область видимости:

x = "до match" data = 123 match data: case x: print(f"Внутри match: {x}") print(f"После match: {x}") > Внутри match: 123 > После match: 123

В примере case x создаёт новую переменную x в текущем блоке. Если x уже существовала до этого — она будет перезаписана. Чтобы избежать таких ситуаций, используйте внутри case уникальные имена переменных или ограничьте область видимости (например, поместите match case в функцию).

Class pattern — объекты и классы

Шаблон позволяет сопоставлять объекты по значениям их атрибутов. Работает только с классами, в которых определены поля для сопоставления — через специальный атрибут __match_args__. Это нужно, чтобы match case понимал, какие поля сравнивать у объекта.

Пример #1 — простое логгирование.

class LogEvent: __match_args__ = ("level", "message") def __init__(self, level, message): self.level = level self.message = message event = LogEvent("error", "Database connection failed") match event: case LogEvent("warning", _): print("⚠️ Предупреждение в журнале") case LogEvent("error", msg): print(f"🚩 Ошибка: {msg}") case _: print("Информация") > 🚩 Ошибка: Database connection failed

В примере мы сопоставляем объект event по его полям level и message, и обрабатываем логику в зависимости от уровня события.

Вместо __match_args__ часто используют декоратор @dataclass — он автоматически добавляет в класс метод __match_args__ для работы с match case.

from dataclasses import dataclass @dataclass # декоратор, автоматически добавляющий поля и методы class Point: x: int y: int p = Point(5, 10) match p: case Point(0, 0): print("📍 Центр координат") case Point(x, y): print(f"🧭 Точка в ({x}, {y})") > 🧭 Точка в (5, 10)
Если у класса нет __match_args__ и не используется @dataclass, конструкция match case не сможет сопоставить объект позиционно — шаблоны просто не сработают.

Комбинирование шаблонов (OR-pattern)

В match case можно использовать оператор | (OR-pattern), чтобы объединить несколько шаблонов и обработать их одним блоком кода. Если подходит хоть один из указанных шаблонов — срабатывает нужный блок.

Простой пример:

command = "пауза" match command: case "пуск" | "старт": print("Запуск процесса") case "стоп" | "пауза": print("Остановка процесса") case _: print("Неизвестная команда") > Остановка процесса

В примере сообщение "Остановка процесса" выведется если command будет "стоп" или "пауза".

✨ Особенности использования OR-pattern:

  • Объединять можно одинаковые по структуре шаблоны — строки, числа, кортежи и т.д.
  • Нельзя комбинировать разные типы шаблонов (например список и словарь).
  • Можно использовать переменные внутри шаблонов — они должны совпадать.
  • Логический оператор & не поддерживается, для этого нужно использовать if внутри case.

💭 Используйте OR-pattern, когда нужно обработать несколько похожих значений одинаково — вместо дублирования кода для каждого case.

Условие в шаблонах (guard-выражение)

В match case можно добавить дополнительное условие с помощью if — это называется guard-выражение. Оно уточняет, при каких условиях должен сработать шаблон. Если шаблон подошёл, но if вернул False — блок не выполнится, и проверка пойдет дальше.

Простой пример:

response = {"status": "ok", "data": []} match response: case {"status": "ok", "data": data} if len(data) > 0: print("Получены данные:", data) case {"status": "ok", "data": []}: print("Данные отсутствуют") case _: print("Ошибка") > Данные отсутствуют

Здесь шаблон сначала проверяет, что словарь содержит нужные ключи, и только если это так, проверяется len(data) > 0.

✨ Особенности использования guard-выражений:

  • Работают только внутри case — за шаблоном.
  • Могут использовать переменные из шаблона (например, case x if x > 0).
  • Можно использовать любые выражения, как в обычном if.
  • Проверка выполняется после сопоставления шаблона.

Вложенные конструкции match case

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

order = { "type": "delivery", "address": {"city": "Москва", "zip": "101000"} } match order: case {"type": "delivery", "address": addr}: match addr: case {"city": city, "zip": _}: print(f"🚚 Доставка в город: {city}") case {"type": "pickup"}: print("🏠 Самовывоз") case _: print("❌ Неизвестный тип заказа") > 🚚 Доставка в город: Москва

Плюсы вложенных match case:

  • Повышается читаемость при работе со сложными структурами (JSON, ответы API и т.д.).
  • Можно пошагово разбирать вложенные уровни данных.
  • Гибкость — каждый уровень можно обработать независимо.

Минусы:

  • Увеличивается вложенность кода.
  • Сложнее отлаживать, если структура глубоко вложена и часто меняется.
  • Нужно аккуратно называть переменные, чтобы не перезаписать во внутренних case.

💡 Совет: если вложенных match становится слишком много — лучше разбейте логику на функции или упростите структуру данных. Так код останется понятным и его будет легче отлаживать.

Типизация при использовании match

Хотя конструкция match сама по себе не использует type hints, типизация полезна вокруг — например, в сигнатурах функций, которые принимают данные для сопоставления.

Просто пример:

def process_response(response: dict) -> str: match response: case {"status": "ok", "data": data}: return f"Получили данные: {data}" case {"status": "error", "message": msg}: return f"Ошибка: {msg}" case _: return "Что-то непонятное"

В данном примере в функцию передается тип dict, а возвращается str.

Если вернуть другой тип, IDE и статические анализаторы (mypy, Pyright) предупредят о возможной ошибке.

Предупреждение в PyCharm: функция с типом str возвращает None в одном из case.

Типизация особенно полезна, если match используется в функциях, которые принимают сложные структуры — например, JSON-ответы, пользовательские объекты и т.д:

from typing import TypedDict class ApiResponse(TypedDict): status: str data: list[str] def handle_response(response: ApiResponse) -> None: match response: case {"status": "ok", "data": data}: print(f"Успех! Получены данные: {data}") case {"status": "error", "message": msg}: print(f"Ошибка: {msg}") case _: print("Непредсказуемый ответ")

✨ Что нужно знать про match case в Python:

  • Работает только в Python 3.10 и выше.
  • Не поддерживается в lambda-функциях.
  • Иногда if проще и понятнее — не всегда нужно усложнять.
  • Для сопоставления объектов нужен корректный класс (__match_args__ или @dataclass).
  • Особенно полезен, когда много вариантов поведения.
  • Работает со строками, числами, списками, словарями и объектами.
  • Поддерживает распаковку, условия, вложенность.
  • Лучше читается при сложной логике — можно воспринимать как визуальную карту данных.

match case особенно полезен когда нужно разбирать фиксированные команды, JSON-ответы, вложенные структуры или списки с разной длиной — match делает код чище и читабельнее. Он отлично подходит, если вариантов поведения много, а структура данных чётко задана. Особенно удобен с @dataclass и распаковкой.

Но match case лучше не использовать, если логика завязана на числовые сравнения (x > 5) или структура данных слишком нестабильна — обычный if будет более гибким и простым. В простых случаях match может только усложнить код, а в критичных по скорости местах лучше использовать словари с функциями.

😭
😕
😃
1
😍
Комментарии
Может понравиться
Оператор выбора в Python
Основы
upd:
Оператор выбора в Python (if else)