یکی از کاربردهای تابع این است که از تکرار کد ها جلوگیری می کند. مثلا شما یک تابع می نویسید که از فایل چیزی را بخواند یا مثلا عدد اول شماره n ام را بدست بیاورد. اینها مثال های ساده ای هستند که همه شما می دانید. اما تا به حال به این فکر کرده اید که گاهی بعضی کارها برای خود توابع هم تکراری می شود و باید یک روش ساده برای جلوگیری از تکرار وجود داشته باشد. مثلا فرض کنید شما می خواهید بدانید هر تابع شما چقدر زمان میگیرد. برای این کار مجبورید چیزی شبیه زیر برای تمام توابع خود بنویسید:
یا اینکه بخواهید لاگ از تابع بگیرید یا حتی آن را به صورت موازی اجرا کنید. برای همه این کارها نیاز هست یک عملیات تکراری را برای تابع انجام دهیم. گویی یک تابع برای تابع نیاز داریم به این صورت که مثلا تابع را به آن پاس بدهیم و آن تابع برایمان زمانش را محاسبه کند یا آن را لاگ کند.
خوشبختانه روش ساده ای برای این کار وجود دارد: دکوراتور ها
یکی از ویژگی های پایتون این است که در آن توابع شهروندان درجه یک هستند! یعنی شما می توانید با آن ها مانند هر شی دیگری برخورد کنید. در پایتون خیلی چیز عجیبی نیست وقتی شما یک تابع را به تابع دیگر پاس بدهید.
همانطور که میبینید می توان تابع را مثل عدد یا هر شی دیگری به صورت آرگومان پاس داد!
از دیگر شگفتی های پایتون این است که شما می توانید تابع در تابع تعریف کنید. برای کسانی که از جاوا یا سی شارپ یا سی می آیند این کار بیشتر شبیه یک عادت بد برنامه نویسی است که باید بشدت از آن اجتناب کرد! اما اینطور نیست! استفاده از تابع در تابع در بسیاری از مواقع نه تنها خوب بلکه لازم است.
فرض کنید می خواهید یک تابع پیچیده را اجرا کنید که شامل بخش های زیادی است. بخشی که فایلی را می خواند بخشی دیگر کلمه ای را در آن پیدا می کند می توانید آن را به صورت زیر بنویسید:
ساده ترین دکوراتور چیزی جز یک تابع تو در تو نیست.
داخل تابع my_decorator یک تابع به اسم wrapper هست که مهمترین وظیفه اش اجرای تابعی است که به my_decorator پاس شده است(خارج کردن گلوله از حلق تفنگ) اما علاوه به آن کارهای دیگری هم می کند (خفه کردن صدای گلوله!!). در اینجا به صورت خیلی ساده فقط قبل و بعد از اجرای تابعی که به آن پاس می شود دو کلمه می نویسد.
سینتکس بالا کمی گیج کننده است به همین خاطر در پایتون یک syntactic sugar وجود دارد که کل چیزی که بالا نوشته شده است را ساده تر می کند:
دکوراتور ها باید طوری نوشته شوند که مستقل از تابعی باشند که نوشته شده است (خفه های خوب بر روی کلت و کلاش و رایفل باید بتوان نصب کرد!). به همین خاطر باید بتوانند آرگومان های تابع را به درستی منتقل کنند. به این ترتیب باید همه آرگومان ها بدون اسم (args) و با اسم (kwargs) را پاس بدهیم.
الان که فهمیدیم چگونه از دکوراتور ها استفاده کنیم چند مثال کاربردی از آن ها را می توانیم حل کنیم. مثلا برای زمان بندی می توان دکوراتور را به صورت زیر نوشت:
همانطور که می بینید استفاده از دکوراتور ها زمانی که منطق آن ها را بفهمید بسیار ساده و به شدت مفید هستند.
امروزه بسیاری از توسعه دهندگان سعی می کنند راه حل های خود را در قالب دکوراتور ها ارايه کنند چون در عمل برای استفاده از آن ها کمترین تغییر در کد نیاز است. یک مورد بسیار جذاب آن کتابخانه ray است که با کمترین تغییرات کد شما را به صورت موازی انجام می دهد.
مثال های بیشتری را می توانید اینجا ببینید.
مثال دیگر آن در tensorflow2 می توان دید. در این نسخه می توان با دکوراتور بدون تغییر در توابع کاری کرد که آن ها بر روی GPU اجرا شوند. این باعث افزایش سرعت بدون درگیر شدن در کدهای پیچیده GPU می شود.