اشکان محمدی
اشکان محمدی
خواندن ۷ دقیقه·۳ سال پیش

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

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

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

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

match 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:

def http_error(status): match status: case 400: return &quotBad request&quot case 404: return &quotNot found&quot case 418: return &quotI'm a teapot&quot case _:             return &quotSomething's wrong with the Internet&quot # from python.org

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

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

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

case 401 | 403 | 404: return &quotNot allowed&quot # copied from python.org

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

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

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

# point is an (x, y) tuple match point: case (0, 0): print(&quotOrigin&quot) case (0, y): print(f&quotY={y}&quot) case (x, 0): print(f&quotX={x}&quot) case (x, y): print(f&quotX={x}, Y={y}&quot) case _: raise ValueError(&quotNot a point&quot) # copied from python.org

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

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

class Point: x: int y: int def location(point): match point: case Point(x=0, y=0): print(&quotOrigin is the point's location.&quot) case Point(x=0, y=y): print(f&quotY={y} and the point is on the y-axis.&quot) case Point(x=x, y=0): print(f&quotX={x} and the point is on the x-axis.&quot) case Point(): print(&quotThe point is located somewhere else on the plane.&quot) case _: print(&quotNot a point&quot) # copied from python.org

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

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

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

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

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

# 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 های تو در تو برای لیست های کوتاه استفاده کنید مثل مثال زیر:

match points: case []: print(&quotNo points in the list.&quot) case [Point(0, 0)]: print(&quotThe origin is the only point in the list.&quot) case [Point(x, y)]: print(f&quotA single point {x}, {y} is in the list.&quot) case [Point(0, y1), Point(0, y2)]: print(f&quotTwo points on the Y axis at {y1}, {y2} are in the list.&quot) case _: print(&quotSomething else is found in the list.&quot) # copied from python.org

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

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

match book_details: case (&quothow to code better&quot, &quotAshkan Mohammadi&quot, &quot21/06/05&quot): print (&quotfound `how to code better` book&quot) case (title, &quotAshkan Mohammadi&quot, _): print (&quotit is one of the books written by Ashkan Mohammadi&quot)     case (&quotGuns&quot, *_): # * is used for packing the rest of the values print (&quotthe book is all about guns and weapons&quot)

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

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

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

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

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

match point: case Point(x, y) if x == y: print(f&quotThe point is located on the diagonal Y=X at {x}.&quot) case Point(x, y): print(f&quotPoint is not on the diagonal.&quot) # 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
آموزشپایتونبرنامه نویسیstructural pattern matchingpython
یه برنامه نویس ساده که از تجربیات و آموخته هاش می نویسه
اینجا جاییه که ما برنامه نویس ها درباره ی خودمون و علاقمندی هامون میگیم. coderlife.ir
شاید از این پست‌ها خوشتان بیاید