فرض کن یه گوشی موبایل خریدی. این گوشی کارش مشخصه: زنگ میزنه، پیام میده و وصل میشه به اینترنت. حالا شما میری براش یه قاب محافظ و یه گلس میخری. آیا ماهیت گوشی عوض شد؟ نه! همون کارهای قبلی رو میکنه، اما حالا قابلیت‌ های جدیدی (مثل ضد ضربه بودن یا زیبایی بیشتر) بهش اضافه شده. دکوراتور در پایتون دقیقا همون قاب موبایله! دکوراتور یه کدی هست که دور یه تابع (Function) دیگه میپیچه تا بدون اینکه کد های اون تابع رو دستکاری کنه، یه سری ویژگی‌ ها و رفتار های جدید بهش اضافه کنه.

 پیش‌نیاز: توابع، شهروندان درجه یک!

برای فهمیدن دکوراتور، فقط باید یه قانون مهم پایتون رو بدونی: توابع تو پایتون مثل متغیرهای معمولی هستن. یعنی چی؟

  • میتونی یه تابع رو بریزی تو یه متغیر.
  • میتونی یه تابع رو به عنوان ورودی بدی به یه تابع دیگه.
  • میتونی از داخل یه تابع، یه تابع دیگه رو خروجی بگیری (Return کنی).

دکوراتور چطور کار می‌کنه؟

بیا با یه مثال ساده شروع کنیم. فرض کن یه تابع داریم که فقط سلام میکنه:

def say_hello():
    print("سلام دنیا!")

حالا می‌خوایم بدون اینکه به کد say_hello دست بزنیم، قبل و بعد از اجراش، یه پیامی چاپ بشه. اینجاست که دکوراتور وارد میشه:

# این همون دکوراتور ماست
def my_awesome_decorator(func):
    def wrapper():
        print("🎉 اتفاقات قبل از اجرای تابع...")
        func() # اینجا تابع اصلی ما اجرا میشه
        print("🎊 اتفاقات بعد از اجرای تابع...")
return wrapper

حالا چطوری این قاب رو بندازیم رو گوشیمون (تابعمون)؟ پایتون یه سینتکس خیلی خوشگل برای این کار داره: استفاده از علامت @ (اَت‌ساین).

@my_awesome_decorator
def say_hello():
    print("سلام دنیا!")
# حالا بیایم تابع رو صدا بزنیم
say_hello()

خروجی این کد چی میشه؟

🎉 اتفاقات قبل از اجرای تابع...
سلام دنیا!
🎊 اتفاقات بعد از اجرای تابع...

اگه تابع ما ورودی (آرگومان) داشت چی؟

توابع تو دنیای واقعی معمولاً ورودی دارن. برای اینکه دکوراتور ما بتونه روی هر تابعی (با هر تعداد ورودی) کار کنه، از جادوی *args و **kwargs استفاده می‌کنیم. نگاه کن:

def smart_decorator(func):
    def wrapper(*args, **kwargs):
        print(">> دارم تابع رو اجرا می‌کنم...")
        result = func(*args, **kwargs)
        print(">> اجرای تابع تموم شد!")
        return result
    return wrapper
@smart_decorator
def add_numbers(a, b):
    return a + b
print(add_numbers(5, 10))

کجاها از دکوراتور استفاده میکنیم؟

دکوراتور ها فقط برای چاپ کردن پیام نیستن! تو پروژه‌ های واقعی (مخصوصاً تو فریم‌ورک‌هایی مثل جنگو یا فلسک)، دکوراتورها ناجی ما هستن تا کدهامون تکراری نشن (قانون DRY). بیا تو جدول زیر چندتا از کاربردهای خفنش رو ببینیم:

کاربرد دکوراتور توضیح خودمونی مثال رایج در برنامه‌نویسی
ورود کاربران (Authentication) چک می‌کنه ببینه کاربر لاگین کرده یا نه. اگه نکرده بود، اصلاً نمیذاره تابع اصلی (مثلا نمایش پروفایل) اجرا بشه. @login_requiredدر جنگو یا فلسک
لاگ‌گیری (Logging) هر بار که تابع اجرا میشه، تو یه فایل می‌نویسه کی، چه ساعتی و با چه ورودی‌هایی این تابع رو صدا زد. @log_action
زمان‌سنجی (Timing) زمان دقیق شروع و پایان تابع رو می‌گیره تا بفهمیم کدمون چقدر کنده یا سریع! @time_it
کش کردن (Caching) جواب تابع رو ذخیره میکنه که اگه دوباره با همون ورودی‌ها صدا زده شد، الکی دوباره محاسبه نکنه و جواب آماده رو بده. @lru_cache در پایتون

جمع‌بندی

دکوراتور ها در واقع توابعی هستن که توابع دیگه رو میگیرن، یه لباس جدید تنشون میکنن و به ما پس میدن. استفاده ازشون باعث میشه کدهامون تمیزتر، خواناتر و حرفه‌ ای ‌تر بشن.