Skip to content

Latest commit

 

History

History
124 lines (93 loc) · 4.96 KB

File metadata and controls

124 lines (93 loc) · 4.96 KB

match / case (сопоставление с образцом)

← Контекстные менеджеры · 🏠 Домой · Исключения →


Чем match отличается от switch в других языках?

Коротко. python 3.10+. Это не выбор по значению, а сопоставление со структурой: match умеет разбирать последовательности, словари и объекты, попутно связывая их части с именами.

# python 3.10+
def handle(command):
    match command.split():
        case ["go", direction] if direction in ("north", "south"):
            return f"идём на {direction}"      # guard-условие
        case ["look"]:
            return "осматриваемся"
        case [action, *rest]:
            return f"{action} с аргументами {rest}"
        case _:
            return "неизвестная команда"

handle("go north")     # 'идём на north'
handle("take sword")   # 'take с аргументами ['sword']'

Глубже. Первый подходящий case выигрывает, дальнейшие не проверяются. case _ — не «default» как ключевое слово, а wildcard-паттерн, который совпадает с чем угодно и ничего не связывает.


Главная ловушка: почему case RED: совпадает с чем угодно?

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

RED = "red"

def h(colour):
    match colour:
        case RED:            # НЕ сравнение с RED!
            return f"поймано: {colour}"

h("blue")   # 'поймано: blue' — ветка сработала на чужом значении

Чтобы сравнить со значением существующего имени, нужен value-паттерн — имя с точкой:

from enum import Enum

class Colour(Enum):
    RED = "red"

match colour:
    case Colour.RED:      # точка -> сравнение, а не захват
        ...
    case "red":           # литерал -> тоже сравнение
        ...
    case x if x == RED:   # либо через guard
        ...

Подвох. Иногда Python ловит эту ошибку сам: если после case RED: есть другие ветки, компилятор откажется собирать модуль с SyntaxError: name capture 'RED' makes remaining patterns unreachable. Но если ветка последняя, ошибки не будет — код молча начнёт работать неправильно.


Какие ещё бывают паттерны?

Классы — сопоставление по типу и атрибутам:

match point:
    case Point(x=0, y=0):
        return "начало координат"
    case Point(x=0, y=y):      # y здесь захватывается
        return f"на оси Y, y={y}"
    case Point():
        return "просто точка"

Если у класса задан __match_args__ (у датаклассов он создаётся сам), можно писать позиционно: case Point(0, y).

Словари — совпадение по подмножеству ключей:

match payload:
    case {"type": "user", "id": int(uid)}:    # заодно проверка типа
        return uid
    case {"type": "error", **rest}:            # rest — остальные ключи
        return rest

Важно: словарный паттерн совпадает, если перечисленные ключи есть; лишние ключи в объекте совпадению не мешают. Это отличается от паттерна последовательности, где длина должна совпадать точно.

Или-паттерны и захват целого:

match code:
    case 200 | 201 | 204:
        return "успех"
    case 400 | 404 as bad:     # as связывает то, что совпало
        return f"ошибка клиента: {bad}"

Подвох. Строка — это последовательность, но match намеренно её так не разбирает: case [first, *rest] не сработает на "abc". То же и для bytes.


← Контекстные менеджеры · 🏠 Домой · Исключения →