Decorators در پایتان

دکوریتور ها باعث تغییر کارایی توابع میشن.مقدمهیکی از مباحث پیشرفته در پایتان، دکوریتور ها هستن. همون طوری که از اسمشون مشخصه کار اصلی شون دکور دادن هست. به عبارت دقیق تر، دکوریتور ها کارایی توابع رو تغییر میدن. تو این مقاله قصد داریم با این ویژگی جذاب پایتان آشنا بشیم. قبل از خود دکوریتور ها باید با یک سری پیش نیاز هایی آشنا بشیم. توصیه میکنم با حوصله این مقاله رو بخونی و صبر کنی تا به خود Decorator ها برسیم.توابع خودشون Object هستن! همیشه عادت کردیم که یک تابع رو تعریف کنیم و وقتی هم میخایم صداش بزنیم جلوش () قرار بدیم ولی از یک دید دقیق تر، خود توابع هم object هستن. تقریبا هر چیزی توی پایتان یک object حساب میشه. بذار با یک کد توضیح بدیم:def my_func(): print(&quotThis is what this function does!&quot) my_func() # This is what this function does!توی کد بالا یک به اسم my_func رو تعریف کردیم و بعدش هم صداش زدیم. حالا اگه بدون استفاده از () فقط اسم تابع رو print بگیریم چی میشه؟!print(my_func) # <function my_func at 0x7f872c7e2d30>حاصل کد بالا چیزی شبیه به همونی عه که جلوش کامنت کردیم. به عبارتی توضیح میده که my_func یک object در قسمتی از حافظه است. حالا که هر تابع یک object هست، پس میشه این object رو توی متغیر دیگه ای هم ذخیره کنیم! یا حتی میشه به عنوان argument به یک تابع دیگه بدیمش. نکته جانبی: وقتی که تابعی رو تعریف می کنیم به متغیر هایی که بهش میدیم میگن parameter ولی وقتی از همون تابع استفاده میکنیم به متغیر هایی که بهش میدیم میگن argument. به عبارتی argument ها مصداق parameter هایی هستن که موقع تعریف تابع مشخص کردیم. اکثرا این دو تا عبارت رو به جای همدیگه استفاده میکنن.توابع به عنوان متغیرتو مثال بالا توضیح دادیم که تابع object هست. حالا میخایم این object رو توی یک متغیر دیگه ذخیره کنیم و از این به بعد از طریق اون متغیر ازش استفاده کنیم:x = my_func x() # This is what this function does!تو مثال بالا میشد به جای x هر کلمه دیگه ای قرار بدیم (تا زمانی که از قوانین نام گذاری متغیر ها در پایتان تبعیت کنه) مثلا: akbar, hasan و ....Wrapper functionsاین امکان وجود داره که داخل یک function هم دوباره یک function تعریف کنیم! به این جور توابع میگن wrapper function. تو انگلیسی wrap کردن یعنی پیچیدن یک چیزی دور یک چیز دیگه. مثلا پیچیدن کاغذ کادو دور هدیه.def f1(func): def wrapper(): print(&quotStarted&quot) func() print(&quotFinished&quot) wrapper() f1(my_func)در کد بالا تابع معمولی f1 رو تعریف کردیم که یک پارامتر میگیره. این پارامتر خودش یک تابعه! داخل تابع f1 یک inner function تعریف میکنیم که wrapper هست. به این علت میگیم بهش wrapper که میاد و دور تابعی که به عنوان parameter دادیم می پیچه و قبل و بعدش دو تا رشته پرینت میگیره. در نهایت داخل تابع f1 تابع wrapper رو که تعریف کرده بودیم صدا میزنیم! اگه کد بالا رو اجرا کنیم حاصلش میشه این:Started This is what this function does! Finishedخوب حالا تو مثال بالا به جای این که تابع wrapper رو مستقیما از داخل f1 صدا بزنیم return اش میکنیم:def f1(func): def wrapper(): print(&quotStarted&quot) func() print(&quotFinished&quot) return wrapper x = f1(my_func) print(x)فکر میکنی نتیجه کد بالا چی باشه؟! مجددا یک رشته ای مثل زیر:<function f1.<locals>.wrapper at 0x7f82bc3710d0>تعجب نکن! اول مقاله گفتیم که برای صدا زدن یک تابع با () جلوش قرار بدیم. اگه به جای print(x) بگیم که:print(x())در اون صورت مجددا حاصل زیر پرینت گرفته میشه:Started This is what this function does! Finishedخلاصه کل کد های بالا میشه این: هر تابعی در پایتان یک object عه که میشه توی variable های دیگه هم ذخیره اش کنیم و تا زمانی که جلوش () قرار ندیم صداش نکردیم. حالا که هر تابعی یک object حساب میشه می تونیم مثل مقادیر دیگه return اش کنیم یا به عنوان پارامتر بدیمش به توابع دیگه. اصلا چیز عجیب و غریبی نیست همون طوری که تا الان int و str رو return می کرد یا میدادیشون به توابع حالا هم با function ها می تونی چنین رفتاری داشته باشی. فقط کافیه جلوشون () نذاری.دکوریتور هاخوب حالا بعد از این همه پیش نیاز بالاخره می رسیم به خود decorator ها. اگه یادت باشه تو کد های بالا وقتی حاصل تابع f1 رو توی متغیر x ذخیره می کردیم برای این که بتونیم حاصل نهایی رو بگیریم باید حتما جلوی x یک () قرار می دادیم. decorator ها باعث میشن که دیگه نیازی نباشه این () رو قرار بدیم! به همین راحتی. به عبارتی هر تابعی رو می تونیم با استفاده از یک دکوریتور، decorate اش کنیم! این طوری:# f1 is a decorator @f1 def my_func(): # This function is decorated by f1 print(&quotThis is what this function does!&quot)توی کد بالا f1 میشه decorator ما و تابع my_func هم به وسیله f1 دکوریت شده. حالا دیگه هر وقت که my_func رو صدا بزنیم بدون نیاز به اضافه بازی های قبلی، حاصلش این میشه:Started This is what this function does Finishedهمون طوری که قول داده بودیم دیدی که f1 به عنوان یک decorator اومده و قبل از تابع مون، Started و بعدش Finished رو پرینت کرده. به عبارتی decorate اش کرد. کاربرد های دیگهدکوریتور ها از مباحث حرفه ای در پایتان هستن. این بحث به خصوص زمانی که با فریمورک هایی مثل Django کار میکنی خیلی بیشتر حس میشه. یکی از کاربر های معروف decorator ها سنجش زمان اجرا شدن یک تابعه. تو مثال بالا فرض کن به جای Started زمان شروع و به جای Finished زمان اتمام رو پرینت می کردیم. یا مثلا قبل از اجرا تابعی که decorate شده نام کاربری که login کرده رو پرینت بگیریم. همون طوری که اول این مقاله قول داده بودیم:دکوریتور ها کارایی یک تابع رو تغییر میدن.نتیجه گیرییک درس مهم تو برنامه نویسی اینه که فکر نکنی همیشه قراره خودت کد بنویسی! شاید یک روزی بخای کد دیگران رو تصحیح کنی یا کدی که قبلا نوشته شده رو اصلاح کنی! مثلا کدی که تو ۱۰۰ خط نوشته شده رو توی ۱۰ خط بنویسی. این جاست که مباحث حرفه ای به کارت میاد. شاید فکر کنی که کتاب خونه هایی مثل Django برات همه این ها رو از قبل تعریف کردن و می تونی هر وقت دوست داشته باشی از پیش تعریف شده های اون ها استفاده کنی ولی اومدیم و یک روزی با مسئله ای رو به رو شدی که با کد از پیش آماده قابل حل نبود! اون موقع چی؟! این جاست که فرق بین یک کد نویس و یک برنامه نویس مشخص میشه. برنامه نویسی یعنی تسلط. در خصوص decorator ها مباحث پیشرفته تری هم هست مثل زمانی که تابعی که میخای decorate اش کنی parameter داشته باشه که تو حوصله این مقاله نیست (هر چند چیز عجیب غریبی هم نیست) و توصیه میکنم از منابع انگلیسی مثل YouTube در موردش مطالعه کنی. در نهایت خیلی ممنون میشم اگه من رو به یک Comment مهمون کنی و در مورد مقاله ام، روش نگارش و نکات تکمیلی نظر بدی.