Конструкция match/case: примеры и фишки для студентов-программистов - 1 Привет, кодерам и будущим гуру Python! Сегодня разберем конструкцию match/case, которая появилась в Python 3.10. Если вы до сих пор пишете бесконечные if/elif/else, то пора прокачать свои скиллы и перейти на что-то более крутое и читаемое. Давайте разбираться, как это работает, на примерах, которые будут понятны даже тем, кто только начал изучать Python. Подключение к базе данных: пример с match/case Представьте, что вам нужно подключиться к базе данных. У вас есть словарь с данными для подключения:

request = {'server': '127.0.0.1', 'login': 'root', 'password': '1234', 'port': 24}
Ваша задача — написать функцию connect_db(), которая проверит данные и вернет строку с результатом подключения. Вот как это можно сделать с помощью match/case:

def connect_db(connect: dict) -> str:
    match connect:
        case {'server': host, 'login': login, 'password': psw, 'port': port}:
            return f"connection: {host}@{login}.{psw}:{port}"
        case {'server': host, 'login': login, 'password': psw}:
            port = 22  # Если порт не указан, используем дефолтный
            return f"connection: {host}@{login}.{psw}:{port}"
        case _:  # Если данные не подходят
            return "error connection"
Здесь мы видим, что в первых двух case есть дублирование кода — строка с возвратом результата. Чтобы не повторяться, можно вынести эту строку за пределы match:

def connect_db(connect: dict) -> str:
    match connect:
        case {'server': host, 'login': login, 'password': psw, 'port': port}:
            pass  # Просто пропускаем, но переменные сохраняются
        case {'server': host, 'login': login, 'password': psw}:
            port = 22  # Дефолтный порт
        case _:  # Если данные не подходят
            return "error connection"
    
    return f"connection: {host}@{login}.{psw}:{port}"
Теперь код стал чище, и мы избавились от дублирования. Если первые два case сработают, то переменные host, login, psw и port будут доступны за пределами match. Если ни один case не подойдет, функция вернет ошибку. Обработка данных о книгах: еще один пример Теперь представим, что у нас есть данные о книгах в разных форматах:

book_1 = ("Юн Цуй", "Рецепты Python", 2022)
book_2 = ["Юн Цуй", "Рецепты Python", 2022, 3432.27]
book_3 = {'author': "Юн Цуй", 'title': "Рецепты Python", 'year': 2022}
book_4 = {'author': "Юн Цуй", 'title': "Рецепты Python", 'price': 3432.27, 'year': 2022}
Наша задача — написать функцию book_to_tuple(), которая приведет все эти данные к одному формату: (автор, название, год, цена). Вот как это можно сделать:

def book_to_tuple(data: dict | tuple | list) -> tuple | None:
    match data:
        case author, title, year:
            price = None  # Если цена не указана
        case author, title, year, price, *_:
            pass  # Игнорируем лишние данные
        case {'author': author, 'title': title, 'year': year, 'price': price}:
            pass
        case {'author': author, 'title': title, 'year': year}:
            price = None  # Если цена не указана
        case _:  # Если данные не подходят
            return None
    
    return author, title, year, price
Здесь мы обрабатываем разные форматы данных: кортежи, списки и словари. Если цена не указана, она становится None. Если данные не подходят ни под один шаблон, функция вернет None. Усложняем задачу: проверка года Теперь добавим проверку на то, что год издания должен быть в диапазоне от min_year до max_year. Сначала сделаем это "в лоб":

def book_to_tuple(data: dict | tuple | list, min_year=1800, max_year=3000) -> tuple | None:
    price = None
    match data:
        case author, title, int(year) if min_year < year < max_year:
            pass
        case author, title, int(year), price, *_ if min_year < year < max_year:
            pass
        case {'author': author, 'title': title, 'year': int(year), 'price': price} if min_year < year < max_year:
            pass
        case {'author': author, 'title': title, 'year': int(year)} if min_year < year < max_year:
            pass
        case _:
            return None
    
    return author, title, year, price
Но тут видно, что проверка года дублируется в каждом case. Чтобы избежать этого, можно вынести проверку за пределы match:

def book_to_tuple(data: dict | tuple | list, min_year=1800, max_year=3000) -> tuple | None:
    price = None
    match data:
        case author, title, int(year):
            pass
        case author, title, int(year), price, *_:
            pass
        case {'author': author, 'title': title, 'year': int(year), 'price': price}:
            pass
        case {'author': author, 'title': title, 'year': int(year)}:
            pass
        case _:
            return None
    
    if not (min_year < year < max_year):
        return None
    
    return author, title, year, price
Теперь код стал чище, и мы избежали дублирования. Фишка с константами в match/case Теперь немного о фишках. Предположим, у вас есть переменная cmd, и вы хотите проверить ее значения:

cmd = 10

match cmd:
    case 3:
        print("3")
    case 5:
        print("5")
Но использовать "магические числа" (типа 3 и 5) — плохая практика. Лучше использовать константы:

CMD_3 = 3
CMD_5 = 5

cmd = 3

match cmd:
    case CMD_3:
        print("3")
    case CMD_5:
        print("5")
Но тут возникает проблема: Python не позволяет использовать переменные как константы в case. Чтобы обойти это, можно использовать хитрость с импортом или классом:

import consts  # Файл с константами

cmd = 3

match cmd:
    case consts.CMD_3:
        print("3")
    case consts.CMD_5:
        print("5")
Или так:

class Consts:
    CMD_3 = 3
    CMD_5 = 5

cmd = 3

match cmd:
    case Consts.CMD_3:
        print("3")
    case Consts.CMD_5:
        print("5")
Итог Конструкция match/case — это мощный инструмент, который позволяет писать более читаемый и лаконичный код. Она особенно полезна, когда нужно обрабатывать разные форматы данных или проверять множество условий. Главное — не бояться экспериментировать и использовать фишки Python на полную катушку! Удачи в кодинге, и помните: чем чище код, тем меньше багов! 🚀