فراتر از switch
از Python 3.10 دستور match/case به زبان اضافه شد. در نگاه اول شبیه switch در زبانهای دیگر است، اما در واقع یک ابزار تطبیق الگوی ساختاری است: نه فقط مقدار، که شکل داده را هم بررسی میکند و اجزای آن را در متغیرها میریزد.
سادهترین شکل: تطبیق مقدار
status = "shipped"
match status:
case "new":
text = "ثبت شده"
case "weaving" | "finishing": # چند مقدار با |
text = "در حال تولید"
case "shipped":
text = "ارسال شد"
case _: # حالت پیشفرض
text = "نامشخص"
برخلاف C، هیچ «سقوط» (fall-through) به case بعدی وجود ندارد و نیازی به break نیست. case _ مثل else عمل میکند و همیشه آخر میآید.
قدرت واقعی: تطبیق ساختار
فرض کنید برنامهای خط فرمان دارید و کاربر دستورهایی مثل add رضایی 3 تایپ میکند. با تطبیق روی لیست کلمات، هم دستور و هم آرگومانها را در یک قدم جدا میکنید:
def handle(command: str) -> str:
match command.split():
case ["add", name, qty] if qty.isdigit():
return f"ثبت {qty} تخته برای {name}"
case ["add", *_]:
return "فرمت: add نام تعداد"
case ["list"] | ["ls"]:
return "فهرست سفارشها"
case ["quit" | "exit"]:
return "خداحافظ"
case []:
return "چیزی ننوشتید"
case _:
return f"دستور ناشناخته: {command}"
print(handle("add رضایی 3")) # ثبت 3 تخته برای رضایی
print(handle("add x")) # فرمت: add نام تعداد
اجزای این مثال: name و qty متغیر گیرنده (capture) هستند و هر مقداری را میگیرند؛ if qty.isdigit() یک guard است که شرط اضافه میگذارد؛ *_ «هر تعداد عضو باقیمانده» را میپذیرد؛ و الگوی لیست فقط وقتی تطبیق میخورد که تعداد اعضا هم جور باشد.
تطبیق دیکشنری (مثلاً دادهی JSON)
def describe(event: dict) -> str:
match event:
case {"type": "order", "qty": int(q)} if q > 10:
return f"سفارش عمده: {q} تخته"
case {"type": "order", "qty": q}:
return f"سفارش: {q}"
case {"type": "cancel", "id": order_id}:
return f"لغو سفارش {order_id}"
case _:
return "رویداد ناشناخته"
الگوی دیکشنری فقط کلیدهای ذکرشده را بررسی میکند و کلیدهای اضافه را نادیده میگیرد. int(q) یعنی «مقدار باید از نوع int باشد و در q قرار گیرد».
کی از match استفاده نکنیم؟
اگر فقط دو سه مقدار ساده را مقایسه میکنید، if/elif کوتاهتر و آشناتر است. match وقتی میدرخشد که داده ساختار دارد: دستورهای چندکلمهای، پیامهای JSON، تاپل مختصات یا درختها.
نکتههایی که کمتر کسی میداند
- دام بزرگ:
case QUIT:که QUIT یک متغیر ساده باشد، مقایسه نمیکند بلکه هر چیزی را میگیرد و در QUIT میریزد! ثابتها باید «نقطهدار» باشند، مثلcase Status.QUIT:یاcase config.QUIT:. - الگوی رشتهای
"abc"با لیست["a","b","c"]تطبیق نمیخورد؛ match عمداً رشته را «دنباله» حساب نمیکند تا غافلگیری پیش نیاید. - با
asمیتوانید کل زیرالگو را هم نگه دارید:case ["add" | "new" as cmd, name]:. - برای گرفتن کلیدهای اضافهی دیکشنری از
**restاستفاده کنید:case {"type": "order", **rest}:. - متغیرهای capture بعد از match هم باقی میمانند؛ حتی در caseی که در نهایت بهخاطر guard رد شده، ممکن است مقدار گرفته باشند.