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.
Вот базовая структура:
match subject:
case pattern1:
# Код, который нужно выполнить, если совпал pattern1
case pattern2:
# Код, который нужно выполнить, если совпал pattern2
case pattern3:
# Код, который нужно выполнить, если совпал pattern3Начнём с простого примера, демонстрирующего базовую идею:
# Простой обработчик кода статуса 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?» Да, можно:
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 имеет несколько преимуществ:
- Более ясное намерение: оператор
matchявно показывает, что вы проверяете одно значение на несколько возможных вариантов - Меньше повторений: вам не нужно повторять имя переменной в каждом сравнении
- Более мощные шаблоны: как мы увидим,
match-caseможет делать гораздо больше, чем простые проверки на равенство - Лучшая читаемость: для сложных деревьев решений
match-caseчасто проще понять
13.1.3) Когда ни один шаблон не совпал
Что произойдёт, если ни один шаблон не совпадёт? Оператор match просто завершится, не выполнив ни один блок case:
# Проверка роли пользователя
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 при обработке пользовательских выборов:
# Система заказов в ресторане
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:
# Обработчик кода статуса 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 после него никогда не выполнятся.
Вот почему подстановка полезна на практике:
# Планировщик по дням недели
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) Литеральные шаблоны: сопоставление конкретных значений
Литеральные шаблоны совпадают с точными значениями. Мы уже использовали их — числа, строки и логические значения являются литеральными шаблонами:
# Контроллер светофора
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 сравнивает и значение, и его тип:
# Валидатор конфигурации (с использованием разных литеральных типов)
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
Иногда нужно выполнить один и тот же код для нескольких разных значений. Можно объединять несколько шаблонов с помощью оператора | (вертикальная черта), который означает «или»:
# Определение выходных
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 с одинаковыми блоками кода.
Можно смешивать разные типы шаблонов с |:
# Валидатор ввода для вопросов да/нет
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 action13.2.4) Определение, какой вариант совпал, с помощью as
При использовании нескольких шаблонов с | вам может понадобиться узнать, какое именно значение совпало. Можно использовать ключевое слово as, чтобы захватить совпавшее значение:
# Обработчик кодов статуса с группированными ответами
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.
Вот ещё пример, показывающий, как это полезно для логирования:
# Обработчик уровней логирования
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 detected13.3) Извлечение значений с помощью переменных привязки
13.3.1) Что такое переменные привязки?
До сих пор мы сопоставляли литеральные значения. Но сопоставление с образцом становится по-настоящему мощным, когда можно захватывать(capture) или извлекать(extract) части данных, которые вы сопоставляете. Переменная привязки(binding variable) (также называемая шаблоном захвата(capture pattern)) — это имя в шаблоне, которое захватывает совпавшее значение и делает его доступным в блоке case.
Вот простой пример:
# Простое захватывание значения
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 подстановки
Давайте сравним переменные привязки и подстановки напрямую:
# Использование подстановки — значение не захватывается
status = 403
match status:
case 200:
print("Success")
case _:
print("Some other status code") # Нельзя получить доступ к фактическому значениюOutput:
Some other status codeТеперь с переменной привязки:
# Использование переменной привязки — значение захватывается
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.
# Система координат — сопоставление шаблона кортежа
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)Разберём, что происходит:
- Объект сопоставления
point— это кортеж(3, 7) - Python проверяет каждый шаблон
caseпо порядку - Первые три шаблона не совпадают, потому что требуют значение
0в конкретной позиции, а в кортеже(3, 7)нет элемента, равного0 - Шаблон
(x, y)совпадает, потому что это кортеж из двух элементов - Python привязывает
xк3, аyк7 - Выполняется блок
caseс этими захваченными значениями
Вот ещё пример, показывающий разные шаблоны кортежей:
# Анализатор 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:
# Команда с аргументами
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.
Вот практический пример с разной длиной списка:
# Обработчик товаров в корзине
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.
# Обработчик профиля пользователя
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}, потому что шаблоны словаря требуют совпадения пар ключ–значение, и это точное совпадение проверяется раньше более общих шаблонов.
Шаблоны словаря гибкие — они совпадают, если указанные ключи существуют с указанными значениями, даже если в словаре есть дополнительные ключи:
# Обработчик ответа 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) Комбинирование литералов и переменных привязки
Можно смешивать литеральные шаблоны и переменные привязки, чтобы создавать сложную логику сопоставления: В отличие от предыдущих примеров с кортежами, где акцент был на сопоставлении структуры и позиции, этот пример показывает, как литеральные значения и переменные привязки можно комбинировать для реализации логики принятия решений.
# Маршрутизатор 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) Практический пример: обработчик событий
Построим полный пример, который демонстрирует силу переменных привязки:
# Обработчик событий игры
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 совпадает только если и шаблон совпал, и охранное условие истинно.
Вот синтаксис:
match subject:
case pattern if condition:
# Код выполняется только если совпал pattern И condition истинноПосмотрим простой пример:
# Контроль доступа по возрасту
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 взаимодействуют с сопоставлением с образцом:
Python сначала проверяет, совпал ли шаблон. Только если шаблон совпал, Python вычисляет охранное условие. Если guard ложен, Python переходит к следующему case — даже если шаблон уже совпал, но guard оказался ложным.
Вот пример, который это демонстрирует:
# Система предупреждений по температуре
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 с литеральными шаблонами, чтобы создавать более специфичные совпадения:
# Калькулятор скидок по типу товара и количеству
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:
# Оценка успеваемости студента с учётом посещаемости
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: SatisfactoryGuard if g >= 70 and a >= 70 требует, чтобы и оценка, и посещаемость были не ниже 70. Поскольку у Боба оценка 85, а посещаемость 75%, этот case совпадает.
13.4.5) Практический пример: система аутентификации пользователей
Построим полный пример, который использует guards для реализации реалистичной системы аутентификации:
# Аутентификация пользователя с доступом по ролям
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 поддерживает это с помощью оператора *, который захватывает ноль или больше элементов.
# Парсер команд с переменным числом аргументов
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 захватывает все элементы середины как список.
Важно: в одном шаблоне последовательности можно использовать только один *-шаблон, и он захватывает элементы как список:
# Парсер записей лога
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 timeout13.5.2) Комбинирование шаблонов последовательностей с guards
Можно использовать guards с шаблонами последовательностей, чтобы добавлять дополнительные условия:
# Анализатор списка оценок
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, когда вы проверяете одно значение по множеству возможностей или работаете со структурированными данными, которым нужно извлечение на основе шаблонов.