
Match case в Python — полный гайд
- Как работает match case в Python
- Шаблоны в case
- Literal pattern — точное совпадение
- Wildcard pattern — по умолчанию
- Sequence pattern — структура списка или кортежа
- Mapping pattern — структура словаря
- Capture pattern — захват значений
- Class pattern — объекты и классы
- Комбинирование шаблонов (OR-pattern)
- Условие в шаблонах (guard-выражение)
- Вложенные конструкции match case
- Типизация при использовании match
В 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.
Рассмотрим простой пример:
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.
Официальная документация по 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) предупредят о возможной ошибке.
Типизация особенно полезна, если 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
может только усложнить код, а в критичных по скорости местах лучше использовать словари с функциями.