Python & AI Tutorials Logo
Программирование Python

13. Принятие решений с match и case (структурное сопоставление с образцом)

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

Сопоставление с образцом(pattern matching) выходит далеко за рамки простых сравнений значений. Оно позволяет сопоставлять структуру и форму данных, извлекать значения из сложных объектов и выражать многовариантные решения в более читаемом виде. Хотя цепочки if-elif-else отлично подходят для многих ситуаций, операторы match-case особенно хороши, когда вы имеете дело с несколькими различимыми случаями, особенно при работе со структурированными данными.

13.1) Знакомство с операторами match и case (на основе if-elif из главы 8)

13.1.1) Базовая структура match-case

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

Вот базовая структура:

python
match subject:
    case pattern1:
        # Код, который нужно выполнить, если совпал pattern1
    case pattern2:
        # Код, который нужно выполнить, если совпал pattern2
    case pattern3:
        # Код, который нужно выполнить, если совпал pattern3

Начнём с простого примера, демонстрирующего базовую идею:

python
# Простой обработчик кода статуса HTTP
status_code = 404
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")

Output:

Error: Page not found

В этом примере оператор match рассматривает status_code (объект сопоставления). Python проверяет каждый шаблон case по порядку. Когда он обнаруживает, что status_code равен 404, он выполняет соответствующий блок кода и затем выходит из оператора match. Оставшиеся варианты больше не проверяются.

13.1.2) Чем match-case отличается от if-elif-else

Вы можете спросить: «Разве нельзя написать это через if-elif-else?» Да, можно:

python
status_code = 404
 
if status_code == 200:
    print("Success: Request completed")
elif status_code == 404:
    print("Error: Page not found")
elif status_code == 500:
    print("Error: Server error")

Output:

Error: Page not found

Обе версии дают один и тот же результат. Однако match-case имеет несколько преимуществ:

  1. Более ясное намерение: оператор match явно показывает, что вы проверяете одно значение на несколько возможных вариантов
  2. Меньше повторений: вам не нужно повторять имя переменной в каждом сравнении
  3. Более мощные шаблоны: как мы увидим, match-case может делать гораздо больше, чем простые проверки на равенство
  4. Лучшая читаемость: для сложных деревьев решений match-case часто проще понять

13.1.3) Когда ни один шаблон не совпал

Что произойдёт, если ни один шаблон не совпадёт? Оператор match просто завершится, не выполнив ни один блок case:

python
# Проверка роли пользователя
user_role = "guest"
 
match user_role:
    case "admin":
        print("Full system access granted")
    case "moderator":
        print("Content management access granted")
    case "editor":
        print("Editing access granted")
 
print("Role check complete")

Output:

Role check complete

Поскольку "guest" не совпадает ни с одним из шаблонов, ни один блок case не выполняется. Программа продолжает работу с кодом после оператора match. Это поведение важно понимать — в отличие от цепочек if-elif-else, где можно добавить заключительную ветку else, чтобы перехватить все остальные случаи, базовый оператор match без шаблона-подстановки молча ничего не сделает, если совпадения не будет.

13.1.4) Практический пример: система выбора из меню

Соберём более полный пример, который демонстрирует наглядность match-case при обработке пользовательских выборов:

python
# Система заказов в ресторане
menu_choice = 3
 
match menu_choice:
    case 1:
        item = "Caesar Salad"
        price = 8.99
        print(f"You ordered: {item} - ${price}")
    case 2:
        item = "Grilled Chicken"
        price = 14.99
        print(f"You ordered: {item} - ${price}")
    case 3:
        item = "Vegetable Pasta"
        price = 12.99
        print(f"You ordered: {item} - ${price}")
    case 4:
        item = "Chocolate Cake"
        price = 6.99
        print(f"You ordered: {item} - ${price}")
 
print("Order submitted to kitchen")

Output:

You ordered: Vegetable Pasta - $12.99
Order submitted to kitchen

Этот пример показывает, что каждый case может содержать несколько операторов. Когда menu_choice совпадает с 3, Python выполняет все три строки в блоке этого case: присваивает item, присваивает price и печатает подтверждение заказа.

13.2) Использование подстановки _, литеральных шаблонов и нескольких шаблонов

13.2.1) Шаблон-подстановка: перехват всего остального

Подчёркивание _ — это специальный шаблон, который совпадает с чем угодно. Обычно его используют как последний case, чтобы обработать все значения, которые не совпали с предыдущими шаблонами — аналогично финальной ветке else в цепочке if-elif-else:

python
# Обработчик кода статуса HTTP со случаем по умолчанию
status_code = 403
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")
    case _:
        print(f"Unhandled status code: {status_code}")

Output:

Unhandled status code: 403

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

Вот почему подстановка полезна на практике:

python
# Планировщик по дням недели
day = "Saturday"
 
match day:
    case "Monday":
        print("Team meeting at 9 AM")
    case "Wednesday":
        print("Project review at 2 PM")
    case "Friday":
        print("Weekly report due")
    case _:
        print(f"{day}: No scheduled events")

Output:

Saturday: No scheduled events

Без шаблона-подстановки, если day было бы "Saturday", "Sunday" или любым другим значением, оператор match завершился бы молча и ничего не вывел. Подстановка гарантирует, что вы корректно обработаете неожиданные или неуказанные случаи.

13.2.2) Литеральные шаблоны: сопоставление конкретных значений

Литеральные шаблоны совпадают с точными значениями. Мы уже использовали их — числа, строки и логические значения являются литеральными шаблонами:

python
# Контроллер светофора
light_color = "yellow"
 
match light_color:
    case "green":
        print("Go")
    case "yellow":
        print("Caution: Light changing soon")
    case "red":
        print("Stop")
    case _:
        print("Invalid light color")

Output:

Caution: Light changing soon

Можно использовать литеральные шаблоны разных типов, и match сравнивает и значение, и его тип:

python
# Валидатор конфигурации (с использованием разных литеральных типов)
setting_value = True
 
match setting_value:
    case True:        # литерал bool
        print("Feature enabled")
    case False:       # литерал bool
        print("Feature disabled")
    case None:        # литерал None
        print("Feature not configured")
    case 0:           # литерал int
        print("Feature explicitly turned off")
    case "auto":      # литерал str
        print("Feature set to automatic mode")
    case _:
        print("Invalid configuration value")

Output:

Feature enabled

Литеральные шаблоны работают с целыми числами, числами с плавающей точкой, строками, логическими значениями и None. Python проверяет равенство по тем же правилам, что и оператор ==.

13.2.3) Несколько шаблонов с оператором OR

Иногда нужно выполнить один и тот же код для нескольких разных значений. Можно объединять несколько шаблонов с помощью оператора | (вертикальная черта), который означает «или»:

python
# Определение выходных
day = "Saturday"
 
match day:
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
        print("It's a weekday - time to work!")
    case "Saturday" | "Sunday":
        print("It's the weekend - time to relax!")
    case _:
        print("Invalid day name")

Output:

It's the weekend - time to relax!

Оператор | позволяет указать несколько шаблонов, которые должны приводить к одному и тому же действию. Если объект сопоставления совпадает с любым шаблоном, разделённым |, выполняется соответствующий case. Это намного чище, чем писать отдельные case с одинаковыми блоками кода.

Можно смешивать разные типы шаблонов с |:

python
# Валидатор ввода для вопросов да/нет
response = "yes"
 
match response:
    case True | "yes":
        print("You confirmed the action")
    case False | "no":
        print("You cancelled the action")
    case _:
        print("Please answer yes or no")

Output:

You confirmed the action

13.2.4) Определение, какой вариант совпал, с помощью as

При использовании нескольких шаблонов с | вам может понадобиться узнать, какое именно значение совпало. Можно использовать ключевое слово as, чтобы захватить совпавшее значение:

python
# Обработчик кодов статуса с группированными ответами
status = 201
 
match status:
    case 200 | 201 | 202 | 204 as success_code:
        print(f"Success: {success_code}")
    case 400 | 401 | 403 | 404 as client_error:
        print(f"Client error: {client_error}")
    case 500 | 502 | 503 as server_error:
        print(f"Server error: {server_error}")
    case _:
        print("Unknown status code")

Output:

Success: 201

Ключевое слово as создаёт переменную привязки(binding variable), которая захватывает любой из совпавших вариантов. В этом примере success_code привязывается к 201, потому что именно это значение совпало среди альтернатив 200 | 201 | 202 | 204.

Вот ещё пример, показывающий, как это полезно для логирования:

python
# Обработчик уровней логирования
log_level = "WARN"
 
match log_level:
    case "DEBUG" | "TRACE" as level:
        print(f"Verbose logging: {level}")
        print("Detailed diagnostic information will be recorded")
    case "INFO" | "NOTICE" as level:
        print(f"Informational: {level}")
        print("Normal operation messages will be recorded")
    case "WARN" | "WARNING" as level:
        print(f"Warning level: {level}")
        print("Potential issues detected")
    case "ERROR" | "FATAL" | "CRITICAL" as level:
        print(f"Error level: {level}")
        print("Immediate attention required")
    case _:
        print("Unknown log level")

Output:

Warning level: WARN
Potential issues detected

13.3) Извлечение значений с помощью переменных привязки

13.3.1) Что такое переменные привязки?

До сих пор мы сопоставляли литеральные значения. Но сопоставление с образцом становится по-настоящему мощным, когда можно захватывать(capture) или извлекать(extract) части данных, которые вы сопоставляете. Переменная привязки(binding variable) (также называемая шаблоном захвата(capture pattern)) — это имя в шаблоне, которое захватывает совпавшее значение и делает его доступным в блоке case.

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

python
# Простое захватывание значения
command = "save"
 
match command:
    case "quit":
        print("Exiting program")
    case action:  # Это переменная привязки
        print(f"Executing action: {action}")

Output:

Executing action: save

Шаблон action — это переменная привязки. Он совпадает с любым значением (как и подстановка _), но, в отличие от _, он захватывает это значение и присваивает его имени action. Внутри блока case вы можете использовать action, чтобы обращаться к совпавшему значению.

Важное различие: переменная привязки совпадает с чем угодно, так же как и _. Разница в том, что _ отбрасывает значение, а переменная привязки захватывает его для использования в блоке case.

13.3.2) Переменные привязки vs подстановки

Давайте сравним переменные привязки и подстановки напрямую:

python
# Использование подстановки — значение не захватывается
status = 403
 
match status:
    case 200:
        print("Success")
    case _:
        print("Some other status code")  # Нельзя получить доступ к фактическому значению

Output:

Some other status code

Теперь с переменной привязки:

python
# Использование переменной привязки — значение захватывается
status = 403
 
match status:
    case 200:
        print("Success")
    case code:  # Переменная привязки захватывает значение
        print(f"Status code {code} received")

Output:

Status code 403 received

Переменная привязки code захватывает значение 403, делая его доступным внутри блока case. Это полезно, когда вам нужно работать с фактическим значением, которое не совпало с вашими конкретными шаблонами.

13.3.3) Сопоставление шаблонов кортежей и извлечение компонентов

Сопоставление с образцом становится особенно мощным при работе со структурированными данными, такими как кортежи(tuple). Вы можете сопоставлять форму кортежа и одновременно извлекать его компоненты. Хотя мы подробно изучим кортежи в главе 15, этот пример сосредоточен только на том, как шаблоны кортежей работают в операторах match.

python
# Система координат — сопоставление шаблона кортежа
point = (3, 7)
 
match point:
    case (0, 0):
        print("Origin point")
    case (0, y):  # Совпадает с любой точкой на оси y
        print(f"On y-axis at y={y}")
    case (x, 0):  # Совпадает с любой точкой на оси x
        print(f"On x-axis at x={x}")
    case (x, y):  # Совпадает с любой другой точкой
        print(f"Point at coordinates ({x}, {y})")

Output:

Point at coordinates (3, 7)

Разберём, что происходит:

  1. Объект сопоставления point — это кортеж (3, 7)
  2. Python проверяет каждый шаблон case по порядку
  3. Первые три шаблона не совпадают, потому что требуют значение 0 в конкретной позиции, а в кортеже (3, 7) нет элемента, равного 0
  4. Шаблон (x, y) совпадает, потому что это кортеж из двух элементов
  5. Python привязывает x к 3, а y к 7
  6. Выполняется блок case с этими захваченными значениями

Вот ещё пример, показывающий разные шаблоны кортежей:

python
# Анализатор RGB-цвета
color = (255, 0, 0)
 
match color:
    case (0, 0, 0):
        print("Black")
    case (255, 255, 255):
        print("White")
    case (r, 0, 0):  # Чистый красный с переменной интенсивностью
        print(f"Pure red with intensity {r}")
    case (0, g, 0):  # Чистый зелёный
        print(f"Pure green with intensity {g}")
    case (0, 0, b):  # Чистый синий
        print(f"Pure blue with intensity {b}")
    case (r, g, b):  # Любой другой цвет
        print(f"RGB color: red={r}, green={g}, blue={b}")

Output:

Pure red with intensity 255

Этот ввод совпадает с шаблоном (r, 0, 0), потому что кортеж содержит три элемента, последние два равны 0, а первое значение привязывается к r.

13.3.4) Сопоставление шаблонов списков

Можно также сопоставлять шаблоны списков(list) и извлекать элементы. Мы подробно рассмотрим списки в главе 14; сейчас этот пример сосредоточен на том, как шаблоны списков работают в операторах match:

python
# Команда с аргументами
command = ["move", "north", "5"]
 
match command:
    case ["quit"]:
        print("Exiting game")
    case ["look"]:
        print("You look around the room")
    case ["move", direction]:
        print(f"Moving {direction}")
    case ["move", direction, distance]:
        print(f"Moving {direction} for {distance} steps")
    case _:
        print("Unknown command")

Output:

Moving north for 5 steps

Шаблон ["move", direction, distance] совпадает со списком из трёх элементов, где первый элемент — "move". Он захватывает второй элемент как direction, а третий — как distance.

Вот практический пример с разной длиной списка:

python
# Обработчик товаров в корзине
item = ["laptop", 999.99, 2]
 
match item:
    case [name]:  # Товар только с названием
        print(f"Item: {name} (no price or quantity specified)")
    case [name, price]:  # Товар с названием и ценой
        print(f"Item: {name}, Price: ${price}, Quantity: 1 (default)")
    case [name, price, quantity]:  # Полная информация о товаре
        total = price * quantity
        print(f"Item: {name}, Price: ${price}, Quantity: {quantity}")
        print(f"Subtotal: ${total}")
    case _:
        print("Invalid item format")

Output:

Item: laptop, Price: $999.99, Quantity: 2
Subtotal: $1999.98

Выполняется case [name, price, quantity], потому что список содержит ровно три элемента, и каждый элемент привязывается к соответствующей переменной.

13.3.5) Сопоставление шаблонов словарей

Сопоставление с образцом работает и со словарями(dictionary), позволяя сопоставлять конкретные ключи и извлекать их значения. Хотя мы подробно изучим словари в главе 16, этот раздел сосредоточен только на том, как шаблоны словарей работают в операторах match.

python
# Обработчик профиля пользователя
user = {"name": "Alice", "role": "admin", "active": True}
 
match user:
    case {"role": "admin", "active": True}:
        print("Active administrator - full access granted")
    case {"role": "admin", "active": False}:
        print("Inactive administrator - access suspended")
    case {"role": role, "active": True}:  # Захват значения role
        print(f"Active user with role: {role}")
    case {"role": role, "active": False}:
        print(f"Inactive user with role: {role}")
    case _:
        print("Invalid user profile")

Output:

Active administrator - full access granted

Срабатывает case {"role": "admin", "active": True}, потому что шаблоны словаря требуют совпадения пар ключ–значение, и это точное совпадение проверяется раньше более общих шаблонов.

Шаблоны словаря гибкие — они совпадают, если указанные ключи существуют с указанными значениями, даже если в словаре есть дополнительные ключи:

python
# Обработчик ответа API
response = {"status": "success", "data": {"id": 123, "name": "Product"}, "timestamp": "2025-12-17"}
 
match response:
    case {"status": "error", "message": msg}:
        print(f"Error occurred: {msg}")
    case {"status": "success", "data": data}:
        print(f"Success! Data received: {data}")
    case _:
        print("Unknown response format")

Output:

Success! Data received: {'id': 123, 'name': 'Product'}

Шаблон {"status": "success", "data": data} совпадает, хотя в словаре есть дополнительный ключ "timestamp". Шаблон требует лишь, чтобы указанные ключи существовали с указанными значениями (или шаблонами).

13.3.6) Комбинирование литералов и переменных привязки

Можно смешивать литеральные шаблоны и переменные привязки, чтобы создавать сложную логику сопоставления: В отличие от предыдущих примеров с кортежами, где акцент был на сопоставлении структуры и позиции, этот пример показывает, как литеральные значения и переменные привязки можно комбинировать для реализации логики принятия решений.

python
# Маршрутизатор HTTP-запросов
request = ("GET", "/api/users", 42)
 
match request:
    case ("GET", "/", None):
        print("Homepage request")
    case ("GET", path, None):
        print(f"GET request for: {path}")
    case ("POST", path, data):
        print(f"POST request to {path} with data: {data}")
    case ("GET", path, user_id):
        print(f"GET request for {path} with user ID: {user_id}")
    case _:
        print("Unsupported request type")

Output:

GET request for /api/users with user ID: 42

Этот пример показывает, как можно сопоставлять конкретные значения (например, "GET") и при этом захватывать другие (например, path и user_id) в одном и том же шаблоне.

13.3.7) Практический пример: обработчик событий

Построим полный пример, который демонстрирует силу переменных привязки:

python
# Обработчик событий игры
event = ("player_move", {"x": 10, "y": 5, "speed": 2})
 
match event:
    case ("player_move", {"x": x, "y": y}):
        print(f"Player moved to position ({x}, {y})")
    case ("player_attack", {"target": target, "damage": damage}):
        print(f"Player attacked {target} for {damage} damage")
    case ("item_pickup", {"item": item_name}):
        print(f"Player picked up: {item_name}")
    case ("game_over", {"score": final_score}):
        print(f"Game ended. Final score: {final_score}")
    case (event_type, data):
        print(f"Unknown event type: {event_type}")
        print(f"Event data: {data}")

Output:

Player moved to position (10, 5)

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

13.4) Добавление дополнительных условий с if guard

13.4.1) Что такое guards?

Иногда нужно сопоставить шаблон и проверить дополнительное условие. Охранное условие(guard) — это дополнительное условие, которое можно добавить к шаблону case с помощью ключевого слова if. Ветка case совпадает только если и шаблон совпал, и охранное условие истинно.

Вот синтаксис:

python
match subject:
    case pattern if condition:
        # Код выполняется только если совпал pattern И condition истинно

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

python
# Контроль доступа по возрасту
age = 16
 
match age:
    case age if age >= 18:
        print("Adult - full access granted")
    case age if age >= 13:
        print("Teen - limited access granted")
    case age if age >= 0:
        print("Child - parental supervision required")
    case _:
        print("Invalid age")

Output:

Teen - limited access granted

В этом примере переменная привязки age захватывает значение, а guard if age >= 13 добавляет дополнительное условие. Ветка case срабатывает только если значение 13 или больше. Поскольку age равно 16, совпадает второй case и выполняется.

13.4.2) Как вычисляются guards

Важно понимать порядок вычисления. Вот подробная визуализация, показывающая, как guards взаимодействуют с сопоставлением с образцом:

Нет

Да

Да

Нет

Да

Нет

Проверить шаблон case

Шаблон совпал?

Попробовать следующий case

Вычислить охранное условие

Guard равен True?

Выполнить блок case

Выйти из оператора match

Есть ещё case?

Совпадений нет — продолжить после match

Python сначала проверяет, совпал ли шаблон. Только если шаблон совпал, Python вычисляет охранное условие. Если guard ложен, Python переходит к следующему case — даже если шаблон уже совпал, но guard оказался ложным.

Вот пример, который это демонстрирует:

python
# Система предупреждений по температуре
temperature = 25
 
match temperature:
    case temp if temp > 35:
        print(f"Extreme heat warning: {temp}°C")
    case temp if temp > 30:
        print(f"High temperature alert: {temp}°C")
    case temp if temp > 20:
        print(f"Comfortable temperature: {temp}°C")
    case temp if temp > 10:
        print(f"Cool temperature: {temp}°C")
    case temp:
        print(f"Cold temperature: {temp}°C")

Output:

Comfortable temperature: 25°C

Каждый case использует переменную привязки temp, чтобы захватить значение температуры, а затем применяет guard, чтобы проверить, попадает ли оно в определённый диапазон. case проверяются по порядку, поэтому выполняется первый совпавший case с истинным guard.

13.4.3) Guards с литеральными шаблонами

Можно сочетать guards с литеральными шаблонами, чтобы создавать более специфичные совпадения:

python
# Калькулятор скидок по типу товара и количеству
item = ("book", 5)
 
match item:
    case ("book", quantity) if quantity >= 10:
        discount = 0.20  # Скидка 20% для 10+ книг
        print(f"Bulk book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity) if quantity >= 5:
        discount = 0.10  # Скидка 10% для 5–9 книг
        print(f"Book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity):
        discount = 0.0  # Нет скидки для менее чем 5 книг
        print(f"Book order: {quantity} books, no discount")
    case (item_type, quantity):
        print(f"Order: {quantity} {item_type}(s)")

Output:

Book order: 5 books, 10.0% discount

Шаблон ("book", quantity) совпадает с кортежем, где первый элемент — "book". Guard if quantity >= 5 добавляет условие, что количество должно быть как минимум 5.

13.4.4) Guards со сложными условиями

Guards могут использовать любое булево выражение, включая сложные условия с and, or и not:

python
# Оценка успеваемости студента с учётом посещаемости
student = {"name": "Bob", "grade": 85, "attendance": 75}
 
match student:
    case {"grade": g, "attendance": a} if g >= 90 and a >= 90:
        status = "Excellent"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 80 and a >= 80:
        status = "Good"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 70 and a >= 70:
        status = "Satisfactory"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 60 or a >= 60:
        status = "Needs Improvement"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case _:
        print("Failing - immediate intervention required")

Output:

Grade: 85, Attendance: 75% - Status: Satisfactory

Guard if g >= 70 and a >= 70 требует, чтобы и оценка, и посещаемость были не ниже 70. Поскольку у Боба оценка 85, а посещаемость 75%, этот case совпадает.

13.4.5) Практический пример: система аутентификации пользователей

Построим полный пример, который использует guards для реализации реалистичной системы аутентификации:

python
# Аутентификация пользователя с доступом по ролям
user = {"username": "alice", "role": "admin", "active": True, "login_attempts": 0}
 
match user:
    case {"active": False}:
        print("Account suspended - contact administrator")
    case {"login_attempts": attempts} if attempts >= 3:
        print("Account locked due to too many failed login attempts")
    case {"role": "admin", "active": True}:
        print("Admin access granted - full system privileges")
    case {"role": "moderator", "active": True}:
        print("Moderator access granted - content management privileges")
    case {"role": role, "active": True} if role in ["editor", "author"]:
        print(f"{role.capitalize()} access granted - content creation privileges")
    case {"role": "user", "active": True}:
        print("User access granted - basic privileges")
    case _:
        print("Access denied - invalid user profile")

Output:

Admin access granted - full system privileges

Этот пример демонстрирует, как guards позволяют реализовать сложную бизнес-логику. Система проверяет несколько условий: статус аккаунта, число попыток входа и права доступа по ролям. Каждый case обрабатывает конкретный сценарий, делая логику аутентификации понятной и поддерживаемой.

13.5) Сопоставление простых форм последовательностей и отображений

13.5.1) Сопоставление последовательностей переменной длины

Иногда нужно сопоставлять последовательности(sequence) различной длины. Сопоставление с образцом в Python поддерживает это с помощью оператора *, который захватывает ноль или больше элементов.

python
# Парсер команд с переменным числом аргументов
command = ["copy", "file1.txt", "file2.txt", "file3.txt", "backup/"]
 
match command:
    case ["help"]:
        print("Available commands: copy, move, delete")
    case ["copy", source, destination]:
        print(f"Copying {source} to {destination}")
    case ["copy", *sources, destination]:
        print(f"Copying {len(sources)} files to {destination}")
        print(f"Source files: {sources}")
    case ["delete", *files]:
        print(f"Deleting {len(files)} file(s): {files}")
    case _:
        print("Unknown command")

Output:

Copying 3 files to backup/
Source files: ['file1.txt', 'file2.txt', 'file3.txt']

Шаблон ["copy", *sources, destination] совпадает со списком, который начинается с "copy", заканчивается пунктом назначения, и имеет любое количество исходных файлов между ними. *sources захватывает все элементы середины как список.

Важно: в одном шаблоне последовательности можно использовать только один *-шаблон, и он захватывает элементы как список:

python
# Парсер записей лога
log_entry = ["2025-12-17", "10:30:45", "ERROR", "Database", "connection", "timeout"]
 
match log_entry:
    case [date, time, "ERROR", *error_details]:
        print(f"Error on {date} at {time}")
        print(f"Error details: {' '.join(error_details)}")
    case [date, time, "WARNING", *warning_details]:
        print(f"Warning on {date} at {time}")
        print(f"Warning details: {' '.join(warning_details)}")
    case [date, time, level, *message]:
        print(f"{level} on {date} at {time}: {' '.join(message)}")

Output:

Error on 2025-12-17 at 10:30:45
Error details: Database connection timeout

13.5.2) Комбинирование шаблонов последовательностей с guards

Можно использовать guards с шаблонами последовательностей, чтобы добавлять дополнительные условия:

python
# Анализатор списка оценок
grades = [85, 92, 78, 95, 88]
 
match grades:
    case []:
        print("No grades recorded")
    case [grade] if grade >= 90:
        print(f"Single excellent grade: {grade}")
    case [grade] if grade < 60:
        print(f"Single failing grade: {grade}")
    case [*all_grades] if len(all_grades) >= 5 and sum(all_grades) / len(all_grades) >= 90:
        average = sum(all_grades) / len(all_grades)
        print(f"Excellent performance! Average: {average:.1f}")
    case [*all_grades] if len(all_grades) >= 5:
        average = sum(all_grades) / len(all_grades)
        print(f"Performance review: {len(all_grades)} grades, Average: {average:.1f}")
    case [*all_grades]:
        print(f"Insufficient data: only {len(all_grades)} grade(s)")

Output:

Performance review: 5 grades, Average: 87.6

Шаблон [*all_grades] захватывает все элементы в списке, а guard проверяет и длину, и вычисляет среднее, чтобы определить подходящее сообщение.


Сопоставление с образцом(pattern matching) с match и case предоставляет мощный, выразительный способ обрабатывать сложные решения в Python. От простого сопоставления значений до продвинутых структурных шаблонов с guards — эта возможность позволяет писать более чистый и удобный для сопровождения код для обработки множества случаев и извлечения данных из сложных структур.

По мере изучения Python вы увидите, что сопоставление с образцом дополняет условную логику, которую вы изучили в главе 8, предлагая элегантную альтернативу при работе с несколькими различимыми случаями, особенно со структурированными данными. Главное — выбирать правильный инструмент для каждой ситуации: используйте if-elif-else для простых условий и булевой логики, и выбирайте match-case, когда вы проверяете одно значение по множеству возможностей или работаете со структурированными данными, которым нужно извлечение на основе шаблонов.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai