ویژگی جدید پایتون 3.10: Structural pattern matching

سلام دوستان امیدوارم حالتون خوب باشه. درین پست قراره Structural pattern matching  که در نسخه 3.10 پایتون به اون اضافه شده ارو مورد برسی قرار بدیم.

برای شما

اگه بخوام به طور خلاصه بگم، خیلی شبیه به switch statement های زبان های برنامه نویسی خانواده C هستش ولی خیلی چیز های خفن تری برای ارائه داره که در ادامه همه رو مورد بحث قرار خواهیم داد

نحوه نوشتار کلی Structural pattern matching :

123456789match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

مثال بالا از خود وبسایت رسمی python.org گرفته شده. همون طور که می بینید این عبارت با کلمه match شروع میشه و در ادامه می تونیم subject رو با case های مختلف انطباق بدیم و روی هر کدوم که منطبق شد، action مربوط به اون قسمت رو انجام میدیم.

در بسیاری از زبان های برنامه نویسی (مخصوصا خانواده C) در انتهای case ها یک عبارت default وجود داره که در صورت عدم انطباق subject بر case ها، کد های مربوط به قسمت default اجرا میشن. در پایتون 3.10 هم اگر به جای استفاده از یک pattern در مقابل case ها، از یک “_” استفاده کنیم، درصورتی که subject به هیچ یک از pattern های موجود منطبق نشه، کد های مربوط به اون قسمت اجرا میشن. این هم ساده ترین استفاده از عبارت های match-case:

12345678910def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case _:
            return "Something's wrong with the Internet" # from python.org

حالا می تونید تابع http_error رو با status code های مختلف فراخوانی کنید و error message های مربوطه رو دریافت کنید.

قابلیت تعریف چند pattern برای یک action

درصورتی که می خواهید عملیاتی مشترک رو برای چند pattern در نظر بگیرید.می تونید در عبارت های match-case از عملگر “|” برای تعریف چند pattern برای یک action استفاده کنید. مثل مثال زیر:

1234case 401 | 403 | 404:
    return "Not allowed"

# copied from python.org

استفاده از pattern هایی با یک بخش literal و یک بخش متغیر

در اکثر زبان هایی که عبارت های switch-case دارند، باید از literal ها یا constant (ثابت) ها استفاده کرد. به طوری که شما نمی تونید در عبارت های switch-case از یک متغیر برای تعریف یک case استفاده کنید. اما پایتون بسیار انعطاف پذیر تر عمل کرده و به شما این امکان رو میده که از متغیر ها هم استفاده کنید.

بخش هایی که literal هستند با مقدار subject مقایسه میشن ولی قسمت های متغیر به صورت wildcard عمل می کنند و به هر مقداری منطبق میشن (تنها کاری که می کنن اینه که مقدار مربوطه رو در خودشون ذخیره می کنند که برای مواقعی که چندین مقدار در subject داریم (مثلا subject یک list یا tuple باشه) بسیار مفیده)

1234567891011121314# point is an (x, y) tuple
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Y={y}")
    case (x, 0):
        print(f"X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")

# copied from python.org

استفاده از class ها به عنوان pattern

اگر برای ذخیره اطلاعات از class ها استفاده می کنید، باید بدونید که میشه class ها رو هم در pattern ها به این صورت استفاده کرد:

123456789101112131415161718class Point:
    x: int
    y: int

def location(point):
    match point:
        case Point(x=0, y=0):
            print("Origin is the point's location.")
        case Point(x=0, y=y):
            print(f"Y={y} and the point is on the y-axis.")
        case Point(x=x, y=0):
            print(f"X={x} and the point is on the x-axis.")
        case Point():
            print("The point is located somewhere else on the plane.")
        case _:
            print("Not a point")

# copied from python.org

همونطور که می بینید کلاس Point دو attribute به نام های x و y داره. حالا اگر بخواهیم مقادیر این دو attribute رو در pattern  ها برسی کنیم، می تونیم اسم attribute ها به همراه مقادیر مورد انتظارمون رو به constructor کلاس Point بدیم.

درضمن درصورتی که می خواهید مقادیر attribute ها رو در متغیر های قابل استفاده در بدنه case ها ذخیره کنید، باید به جای مقدار اون attribute از یک identifier برای نام متغیر استفاده کنید. مثل مثال زیر:

1234case Point(x=xValue, y=yValue):
    #      ^         ^ : actual attribute names along with variable names
    print (f"{xValue} {yValue}")
    #         ^^^^^^   ^^^^^^ : use variables

مشخص کردن ترتیب attribute های class

به صورت پیش فرض وقتی شما دارید از class ها در pattern ها استفاده می کنید، باید اسم همه attribute هایی رو که می خواهید مورد برسی قرار بدید رو مشخص کنید. اما بعضی اوقات این اذیت کننده میشه و شاید ترجیح بدید از positional parameters استفاده کنید. در اون صورت باید به __match_args__ در کلاس مورد نظر مقدار بدید. مثلا اگه مقدار این attribute رو در کلاس Point برابر با (“x”, “y”) بزارید. اون موقع می تونید به این صورت از کلاس Point استفاده کنید:

123456789# checks if x is equal to 1 and assigns the value of `y` to var
Point(1, var)
#     ^  ^^^
#     x, y

# all do the same  thing
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

استفاده از pattern های تو در تو یا nested

شما می تونید از pattern های تو در تو برای لیست های کوتاه استفاده کنید مثل مثال زیر:

12345678910111213match points:
    case []:
        print("No points in the list.")
    case [Point(0, 0)]:
        print("The origin is the only point in the list.")
    case [Point(x, y)]:
        print(f"A single point {x}, {y} is in the list.")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
    case _:
        print("Something else is found in the list.")

# copied from python.org

ساخت pattern های پیچیده با tuple ها و wildcard

شما می تونید از wildcard ها حتی در pattern های پیچیده تر استفاده کنید.

1234567match book_details:
    case ("how to code better", "Ashkan Mohammadi", "21/06/05"):
        print ("found `how to code better` book")
    case (title, "Ashkan Mohammadi", _):
        print ("it is one of the books written by Ashkan Mohammadi")
    case ("Guns", *_): # * is used for packing the rest of the values
        print ("the book is all about guns and weapons")

متغیرbook_details یک tuple است که شامل (title, author, publication_date) یک کتاب میشه.

هرجایی که از “_” استفاده شده به این معنیه که مهم نیست در اینجا چه مقداری قرار داره.

“_” یک identifier صحیح محسوب میشه، یعنی شما می تونید متغیری رو تنها با “_” نام گذاری کنید. پس در واقع “_” یک متغیره ولی معنی “_” در بین برنامه نویس های پایتون به این معنیه که: این مقدار رو نادیده بگیر چون اهمیتی نداره

استفاده از guard ها

شما می تونید یک if clause به آخر case ها اضافه کنید که بهش guard میگن. اگر مقدار guard برابر با False باشه، پایتون از کل case (علی رغم این که منطبق شده باشه) صرف نظر می کنه و میره سراغ بعدی. توجه داشته باشید که انتساب مقادیر به متغیر ها قبل از چک کردن شرط if انجام میشه. یعنی می تونید از متغیر هایی که در pattern تعریف کردید در guard هم استفاده کنید:

1234567match point:
    case Point(x, y) if x == y:
        print(f"The point is located on the diagonal Y=X at {x}.")
    case Point(x, y):
        print(f"Point is not on the diagonal.")

# copied from python.org


خوب دوستان به انتهای این پست رسیدیم. امیدوارم براتون مفید بوده باشه. برای اطلاعات بیشتر می تونید به این صفحه مراجعه کنید.

شاید از این نوشته هم خوشتون اومد:

https://virgool.io/@mohammadiashkan1384/%D8%B9%D9%85%D9%84%DA%AF%D8%B1-%D8%AF%D8%B1-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-m5bs832cvalp

عملگر =, در پایتون !؟
عملگر =, در پایتون !؟
آیا تا به حال به این عملگر برخورد کرده اید؟

نویسنده مطلب: اشکان محمدی

منبع مطلب

به فکر سرمایه‌گذاری هستی؟

با هر سطحی از دانش در سریع‌ترین زمان با آموزش گام به گام، سرمایه گذاری را تجربه کن. همین الان میتونی با لینک زیر ثبت نام کنی و ۱۰ درصد تخفیف در کارمزد معاملاتی داشته باشی

ثبت نام و دریافت جایزه
ممکن است شما بپسندید
نظر شما درباره این مطلب

آدرس ایمیل شما منتشر نخواهد شد.