Приклади match/case в Python 3.10 з поясненням
Реліз Python 3.10, що вийшов у жовтні 2021 року, запропонував розробникам кілька цікавих змін, включно з pattern matching statement (оператор співставлення з шаблонами). Як запевняли автори PEP 622, на створення цієї пропозиції їх надихнув схожий синтаксис у мовах програмування Scala і Erlang.
Якщо ви ще не знайомі з pattern matching, то зараз у вас є хороша можливість дізнатися в цій статті, що виконує ця функція, як відбувається збіг, призначення та варіанти використання цього оператора в Python.
Pattern matching: що це таке і навіщо він потрібний
Відповідно до пояснень, викладених у PEP622, в коді Python іноді потрібно співставляти дані з типами, звертатися до них за індексом та застосовувати перевірку на тип. При цьому необхідно перевіряти не лише тип даних, але й їхнє число. А це призводить до того, що виникає велика кількість відгалужень if/else, які викликають функції isinstance, len і звертаються до елементів за індексом, атрибутом або ключем. Щоб уникнути подібних ускладнень та скоротити if/else, з’явився оператор Pattern matching.
До виходу Python 3.10 в його синтаксисі був простий і надійний спосіб порівняння значень з однією з безлічі можливих умов. Хоча в інших мовах такі фічі давно існували. Наприклад, у C і C++ є конструкція switch/case. Щось подібне є і в Rust.
Pattern matching у коді Python реалізується за допомогою конструкції match/case. Вона дозволяє співставляти вираз, структуру даних або тип шаблону. Простіше кажучи, Python перебирає всі варіанти шаблонів, доки знайде той шаблон, який відповідає виразу.
Важливо не плутати match/case та switch/case. Відмінності між ними полягають в тому, що pattern matching та його конструкція match/case — це не звичайний оператор для порівняння якоїсь змінної зі значеннями, а готовий механізм для співставлення даних, їхнього розпакування та управління потоком виконання.
Використовувати match/case можна в таких ситуаціях:
- для спрощення складних розгалужень (тепер не потрібні численні перевірки if/elif);
- для співставлення складних структур даних із шаблоном;
- для перевірки типу змінної та обробки різних типів даних у кількох варіантах.
Тепер час настав для вивчення кількох прикладів, як цей оператор полегшує написання і роботу з кодом, роблячи його більш читабельним.
Приклад 1: Просте співставлення значень
def day_type(day): match day: case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday": return "Weekday" case "Saturday" | "Sunday": return "Weekend" case _: return "Invalid day" print(day_type("Saturday")) # Output: Weekend
Приклад 2: Порівняння з типами даних
def describe_value(value): match value: case int(): return "This is an integer." case str(): return "This is a string." case list(): return "This is a list." case _: return "Unknown type." print(describe_value(42)) # Output: This is an integer.
Приклад 3: Співставлення з шаблоном у списку
def process_list(data): match data: case [a, b, c]: return f"List of three elements: {a}, {b}, {c}" case [a, b]: return f"List of two elements: {a}, {b}" case []: return "Empty list" case _: return "Different structure" print(process_list([1, 2, 3])) # Output: List of three elements: 1, 2, 3
Приклад 4: Порівняння з шаблоном у словнику
def handle_request(request): match request: case {"method": "GET", "path": path}: return f"Handle GET request to {path}" case {"method": "POST", "path": path, "data": data}: return f"Handle POST request to {path} with data: {data}" case _: return "Unknown request format" print(handle_request({"method": "POST", "path": "/submit", "data": {"key": "value"}})) # Output: Handle POST request to /submit with data: {'key': 'value'}
Приклад 5: Співставлення вкладених структур даних
def parse_response(response): match response: case {"status": 200, "data": {"id": id, "name": name}}: return f"Success! ID: {id}, Name: {name}" case {"status": 404}: return "Not Found" case {"status": 500, "error": error}: return f"Server Error: {error}" case _: return "Unknown response format" print(parse_response({"status": 200, "data": {"id": 123, "name": "Alice"}})) # Output: Success! ID: 123, Name: Alice
Складний приклад коду з використанням match/case у Python
А зараз давайте перейдемо від простих прикладів коду до складних варіантів застосування match/case в Python. Фрагмент, який ви зараз побачите, демонструє аналіз складної структури даних та включає порівняння вкладених структур даних, використання умовних перевірок (guards) та вилучення значень.
Припустимо, у нас є система, яка призначена для обробки різних типів повідомлень у вигляді списків та вкладених словників. Всі повідомлення мають тип і здатні містити дані та метадані. Нам потрібно обробити всі типи повідомлень у різний спосіб, у тому числі з виконанням умовних перевірок.
def process_message(message): match message: case {"type": "user_action", "action": action, "details": {"user_id": user_id, "timestamp": timestamp}}: return f"User {user_id} performed {action} at {timestamp}" case {"type": "system_event", "event": event, "metadata": {"severity": severity}} if severity > 3: return f"Critical system event: {event} with severity {severity}" case {"type": "notification", "content": {"title": title, "message": msg}, "recipients": recipients}: if "admin" in recipients: return f"Admin notification: {title} - {msg}" else: return f"User notification: {title} - {msg}" case {"type": "batch", "messages": messages}: results = [process_message(msg) for msg in messages] return f"Batch processed with results: {results}" case _: return "Unknown message format" # Приклад повідомлень для обробки message1 = { "type": "user_action", "action": "login", "details": {"user_id": 42, "timestamp": "2024-08-22T14:00:00Z"} } message2 = { "type": "system_event", "event": "disk_failure", "metadata": {"severity": 5} } message3 = { "type": "notification", "content": {"title": "Maintenance", "message": "Scheduled maintenance at midnight"}, "recipients": ["admin", "user_123"] } message4 = { "type": "batch", "messages": [message1, message2, message3] } # Обробка повідомлень print(process_message(message1)) # Output: User 42 performed login at 2024-08-22T14:00:00Z print(process_message(message2)) # Output: Critical system event: disk_failure with severity 5 print(process_message(message3)) # Output: Admin notification: Maintenance - Scheduled maintenance at midnight print(process_message(message4)) # Output: Batch processed with results: [...]
Давайте розберемо, що ми бачимо у цьому коді.
- case 1: Виконує співставлення повідомлення типу “user_action” із вкладеною структурою даних. Вилучаються значення action, user_id та timestamp. Потім вони використовуються у рядку результату.
- case 2: Виконує співставлення повідомлення типу “system_event”, але якщо значення severity перевищує 3 (умова, позначене з допомогою if severity > 3).
- case 3: Порівнює повідомлення типу “notification”. Залежно від наявності “admin” у списку одержувачів (recipients) формується різне повідомлення.
- case 4: Обробляє повідомлення типу batch, що містять список повідомлень. Для кожного повідомлення всередині списку рекурсивно викликається функція process_message і результати збираються до списку.
- case _: Збирає всі повідомлення, які не відповідають жодному з попередніх шаблонів, і повертає повідомлення про помилку.
Обробка даних у системі розумного будинку за допомогою match/case у Python
Припустимо, нам доручили створити систему управління розумним будинком. Система отримує дані від датчиків, встановлених у різних приміщеннях. Вони передають інформацію про температуру, вологість, рівень освітлення та інші дані. Потім всі ці повідомлення надходять до пристроїв, які, швидше за все, мають різну структуру. Наше завдання полягає в тому, щоб ефективно обробити повідомлення від датчиків, використовуючи конструкцію match/case у Python.
Відповідно до умов задачі для кожного типу датчика потрібно:
- Отримати дані з пристрою.
- Змінити роботу пристрою за потреби (підвищити або знизити температуру, увімкнути освітлення, надіслати повідомлення).
- Реалізація повинна бути виконана за допомогою match/case у Python 3.10.
def process_sensor_data(sensor_data): match sensor_data: # Повідомлення від датчика температури case {"type": "temperature", "value": temp, "unit": "C"} if temp > 30: return f"Warning: High temperature detected: {temp}°C. Turning on the AC." case {"type": "temperature", "value": temp, "unit": "C"}: return f"Temperature is normal: {temp}°C." # Повідомлення від датчика вологості case {"type": "humidity", "value": humidity, "unit": "%"} if humidity > 70: return f"Warning: High humidity detected: {humidity}%. Activating dehumidifier." case {"type": "humidity", "value": humidity, "unit": "%"}: return f"Humidity is normal: {humidity}%." # Повідомлення від датчика руху case {"type": "motion", "detected": True, "location": location}: return f"Motion detected in {location}. Turning on the lights." case {"type": "motion", "detected": False}: return "No motion detected." # Повідомлення від датчика освітлення case {"type": "light", "value": level, "unit": "lux"} if level < 50: return f"Low light level detected: {level} lux. Turning on the lights." case {"type": "light", "value": level, "unit": "lux"}: return f"Light level is sufficient: {level} lux." # Обробка невідомих типів повідомлень case _: return "Unknown sensor data received." # Приклади вхідних даних sensor_data1 = {"type": "temperature", "value": 32, "unit": "C"} sensor_data2 = {"type": "humidity", "value": 75, "unit": "%"} sensor_data3 = {"type": "motion", "detected": True, "location": "living room"} sensor_data4 = {"type": "light", "value": 30, "unit": "lux"} # Обробка даних print(process_sensor_data(sensor_data1)) # Output: Warning: High temperature detected: 32°C. Turning on the AC. print(process_sensor_data(sensor_data2)) # Output: Warning: High humidity detected: 75%. Activating dehumidifier. print(process_sensor_data(sensor_data3)) # Output: Motion detected in living room. Turning on the lights. print(process_sensor_data(sensor_data4)) # Output: Low light level detected: 30 lux. Turning on the lights.
Потрібно пояснити, що ми бачимо в цьому коді. Спочатку відбувається обробка повідомлень, отриманих від датчиків температури:
- Якщо температура перевищує 30°C, система розумного будинку створює попередження та включає кондиціонер.
- Якщо температура відповідає нормі, з’являється повідомлення про це.
Обробка повідомлень від датчиків вологості:
- При перевищенні показника вологості 70% потрібно включити осушувач та створити про це повідомлення.
- Якщо вологість відповідає нормі, з’являється повідомлення про це.
Обробка повідомлень від датчиків руху:
- При виявленні руху у приміщенні система включає освітлення у зазначеному місці.
- Якщо рух не фіксується, відображається повідомлення про відсутність руху.
Обробка даних від датчиків світла:
- При рівні освітленості нижче 50 люкс система повинна включити світло.
- Якщо рівень освітлення є прийнятним, виводиться повідомлення.
Обробка невідомих типів повідомлень:
- Якщо повідомлення не відповідає жодному з відомих типів, виводиться повідомлення про помилку.
Переваги використання match/case у цьому прикладі:
- Чистота та читабельність коду: завдяки конструкції match/case можна висловити логіку обробки повідомлень структурно та лаконічно.
- Гнучкість: використовуючи guards (if), можна змінювати як структуру даних, і їх значення.
- Розширюваність: додавання нових типів датчиків та нових умов обробки відбувається без істотних змін коду.
Висновок
Як бачите, приклади демонструють, що за допомогою конструкції match/case можна успішно керувати логікою обробки даних з використанням вкладених структур, умовних перевірок та рекурсії. Вона не тільки робить код більш читабельним, завдяки їй можна також його значно спростити.
Favbet Tech – це ІТ-компанія зі 100% украінською ДНК, що створює досконалі сервіси для iGaming і Betting з використанням передових технологіи та надає доступ до них. Favbet Tech розробляє інноваційне програмне забезпечення через складну багатокомпонентну платформу, яка здатна витримувати величезні навантаження та створювати унікальний досвід для гравців.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: