Сопоставление с образцом: оператор match в Python

Раздел: Python -> Управляющие конструкции

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

match subject:
    case pattern1:
        # действия для pattern1
    case pattern2:
        # действия для pattern2
    case _:
        # действия по умолчанию

оператор case в python (оператор case (match) в python)

Основной пример: обработка числового значения

Самый частый сценарий – замена цепочки if-elif на match. Рассмотрим функцию, возвращающую название дня недели по номеру:

def day_name(day: int) -> str:
    match day:
        case 1:
            return "Понедельник"
        case 2:
            return "Вторник"
        case 3:
            return "Среда"
        case 4:
            return "Четверг"
        case 5:
            return "Пятница"
        case 6:
            return "Суббота"
        case 7:
            return "Воскресенье"
        case _:
            return "Неизвестный день"

Match case python (конструкция match-case в python)

Каждый case проверяет значение subject на равенство. Ветка _ срабатывает, если ни один из предыдущих паттернов не подошёл. Это эффективно заменяет if-elif-else.

Как реализовать сопоставление значений без использования match?

Классическая конструкция if-elif-else:

def day_name_if(day: int) -> str:
    if day == 1:
        return "Понедельник"
    elif day == 2:
        return "Вторник"
    elif day == 3:
        return "Среда"
    elif day == 4:
        return "Четверг"
    elif day == 5:
        return "Пятница"
    elif day == 6:
        return "Суббота"
    elif day == 7:
        return "Воскресенье"
    else:
        return "Неизвестный день"

операторы ветвления в языке python (условные операторы в python)

Разница в читаемости невелика, но match выигрывает, когда нужно одновременно анализировать несколько переменных или структуру.

Как применить словарь для выбора действия по значению?

Словарная диспетчеризация – ещё один способ уйти от длинных if. Ключи – ожидаемые значения, значения – вызываемые объекты или строки:

day_names = {1: "Понедельник", 2: "Вторник", 3: "Среда", 4: "Четверг",
             5: "Пятница", 6: "Суббота", 7: "Воскресенье"}
def day_name_dict(day: int) -> str:
    return day_names.get(day, "Неизвестный день")

Return s s python (оператор return в python)

Такой подход очень быстр, но не позволяет проверять типы или структуры данных.

Как проверить структуру составного объекта без match?

До появления match для анализа кортежей или списков приходилось распаковывать и проверять каждый элемент вручную:

point = (10, 20)
if isinstance(point, tuple) and len(point) == 2 and point[0] == 0:
    print("Точка на оси Y")
elif isinstance(point, tuple) and len(point) == 2 and point[1] == 0:
    print("Точка на оси X")
else:
    print("Обычная точка")

С match та же логика записывается лаконичнее (см. examples_adv).

Типичные ошибки и способы их решения

  1. Неполное покрытие. Если не указана ветка _, а ни один паттерн не совпал, Python выбрасывает MatchError. Решение – всегда добавлять case _: (или использовать | для полного перекрытия).
  2. Порядок веток. Первый совпавший case выполняется, остальные игнорируются. Специфичные паттерны должны идти до общих. Например, case _: в начале перекроет все остальные.
  3. Привязка переменной. Внутри case можно присвоить значение части объекта (например, case x:). Если после этого попытаться использовать x вне case, будет ошибка NameError. Решение – объявить переменную заранее или использовать as с явным захватом.
  4. Guard (if) с побочными эффектами. Условие в guard выполняется каждый раз при проверке ветки. Не стоит помещать туда вызовы, меняющие состояние программы.
  5. Сравнение с None, True, False. Эти константы являются паттернами, а не литералами. Поэтому case None: сработает именно на объект None, а не на строку "None". Это удобно, но может запутать новичков.

Сопоставление с образцом открывает новые возможности для обработки структурных данных и сокращает объём шаблонного кода.

- как на языке python записывается полное ветвление (полное ветвление в python)
- циклы в python примеры (примеры циклов в python)
- программа с циклом while python (программа с циклом while на python)

Расширенные примеры использования match

Пример 1: сопоставление с кортежами разного размера

Пример
def describe_point(pt):
    match pt:
        case (0, 0):
            return "Начало координат"
        case (0, y):
            return f"На оси Y, y={y}"
        case (x, 0):
            return f"На оси X, x={x}"
        case (x, y):
            return f"Точка ({x}, {y})"
        case _:
            return "Не точка (не кортеж из двух элементов)"

print(describe_point((0, 5)))
print(describe_point((3, 0)))
print(describe_point((2, 7)))
print(describe_point("строка"))
На оси Y, y=5
На оси X, x=3
Точка (2, 7)
Не точка (не кортеж из двух элементов)

Паттерны с привязкой переменных (y, x) позволяют сразу извлечь значения. Ветка _ обрабатывает любой другой тип.

Пример 2: работа со списками и отбрасывание элементов

Пример
def analyze_list(lst):
    match lst:
        case []:
            return "Пустой список"
        case [first, *rest]:
            return f"Первый элемент: {first}, остальные: {rest}"
        case _:
            return "Не список"

print(analyze_list([1, 2, 3]))
print(analyze_list([]))
Первый элемент: 1, остальные: [2, 3]
Пустой список

Звёздочка *rest захватывает оставшуюся часть списка. Если нужен только первый элемент, можно использовать case [first, *_]:.

Пример 3: сопоставление с классами и атрибутами

Пример
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def what_shape(obj):
    match obj:
        case Point(x=0, y=0):
            return "Точка в начале координат"
        case Point():
            return f"Обычная точка с x={obj.x}, y={obj.y}"
        case Circle(center=Point(x, y), radius=r):
            return f"Окружность с центром ({x},{y}) и радиусом {r}"
        case _:
            return "Неизвестная фигура"

p = Point(0, 0)
c = Circle(Point(3, 4), 5)
print(what_shape(p))
print(what_shape(c))
Точка в начале координат
Окружность с центром (3,4) и радиусом 5

Паттерны классов проверяют тип объекта и могут извлекать значения атрибутов по именам. Вложенные паттерны (как Circle(center=Point(x, y), ...)) проверяют сразу структуру.

Пример 4: использование оператора OR (|)

Пример
def classify_code(c):
    match c:
        case 200 | 201 | 204:
            return "Успех"
        case 400 | 404:
            return "Ошибка клиента"
        case 500 | 502 | 503:
            return "Ошибка сервера"
        case _:
            return "Другой код"

print(classify_code(200))
print(classify_code(404))
Успех
Ошибка клиента

Вертикальная черта объединяет несколько литералов в один паттерн. Все они должны быть константами (или захваченными переменными).

Пример 5: захват всего совпадения через as

Пример
def log_value(val):
    match val:
        case (0 as zero) | (0.0 as zero):    # синтаксис: as нельзя в OR напрямую
            return f"Обнаружен ноль: {zero}"
        # правильный вариант: case (0 | 0.0) as zero:
        # но Python 3.10-3.12 допускает только case  as name
    # Вместо этого используем guard:
    # pass

На самом деле использовать as в OR некорректно. Правильная форма – case (0 | 0.0) as zero:. Пример ниже демонстрирует корректный захват:

Пример
def analyze_item(item):
    match item:
        case tuple() as t:
            return f"Кортеж длиной {len(t)}"
        case list() as lst:
            return f"Список длиной {len(lst)}"
        case _:
            return "Другой тип"

print(analyze_item((1, 2)))
print(analyze_item([5]))
Кортеж длиной 2
Список длиной 1

Пример 6: guard (дополнительное условие)

Пример
def parse_number(s):
    match s:
        case str() if s.isdigit():
            return int(s)
        case str() if s.replace('.', '', 1).isdigit():
            return float(s)
        case _:
            return None

print(parse_number("123"))
print(parse_number("3.14"))
print(parse_number("abc"))
123
3.14
None

Guard позволяет наложить дополнительное условие после if. Он вычисляется только если паттерн совпал.

Пример 7: вложенный match (сопоставление внутри сопоставления)

Пример
def deep_match(data):
    match data:
        case ('point', (0, 0)):
            return "Начало координат"
        case ('point', (x, y)) if x == y:
            return f"Точка на диагонали ({x},{y})"
        case ('circle', {'center': (x, y), 'radius': r}):
            return f"Окружность с центром ({x},{y}) и радиусом {r}"
        case _:
            return "Неизвестная структура"

print(deep_match(('point', (5, 5))))
print(deep_match(('circle', {'center': (1,2), 'radius': 3})))
Точка на диагонали (5,5)
Окружность с центром (1,2) и радиусом 3

Паттерны могут включать словари, кортежи, списки и произвольную вложенность. match автоматически проверяет и извлекает нужные части.

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

Оператор case (match) в Python - comments

En
оператор case в python (python)