Python Decorators

در پایتون، Decorator (دکوراتور) یک تابع است که تابع یا متد دیگری را دریافت کرده، آن را تغییر می‌دهد یا قابلیت‌های جدیدی به آن اضافه می‌کند، بدون این‌که کد اصلی تابع تغییر کند.
دکوراتورها ابزاری قدرتمند برای افزودن رفتار تکراری (مثل لاگ‌گیری، احراز هویت، کش کردن و …) هستند.

🔹 مفهوم پایه

  • تابع دکوراتور: تابعی است که تابع دیگری را به عنوان ورودی می‌گیرد و یک تابع جدید برمی‌گرداند.

  • از علامت @ برای استفاده راحت از دکوراتور روی یک تابع استفاده می‌کنیم.

🔹 مثال ساده

def my_decorator(func): def wrapper(): print("قبل از اجرای تابع اصلی") func() print("بعد از اجرای تابع اصلی") return wrapper @my_decorator def say_hello(): print("سلام دنیا!") say_hello()

📌 خروجی:

قبل از اجرای تابع اصلی سلام دنیا! بعد از اجرای تابع اصلی

🔹 معادل بدون علامت @

همان مثال را می‌توان بدون @ هم نوشت:

decorated = my_decorator(say_hello) decorated()

🔹 دکوراتور با آرگومان

اگر تابع اصلی آرگومان داشته باشد، دکوراتور باید آن‌ها را دریافت و منتقل کند:

def my_decorator(func): def wrapper(*args, **kwargs): print("شروع") result = func(*args, **kwargs) print("پایان") return result return wrapper @my_decorator def add(x, y): return x + y print(add(3, 5))

📌 خروجی:

شروع پایان 8

🔹 دکوراتور با آرگومان شخصی (Decorator Factory)

گاهی می‌خواهیم خود دکوراتور ورودی بگیرد:

def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) def hello(): print("سلام") hello()

📌 خروجی:

سلام سلام سلام

🔹 استفاده‌های متداول

  • لاگ‌گیری (Logging): ثبت زمان اجرا و نتایج توابع.

  • احراز هویت (Authentication): بررسی دسترسی کاربر قبل از اجرای تابع.

  • کش کردن (Caching): ذخیره نتایج توابع برای اجرای سریع‌تر.

  • زمان‌سنجی (Timing): اندازه‌گیری مدت اجرای یک تابع.

مثال زمان‌سنجی:

import time def timing(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"زمان اجرا: {end - start:.4f} ثانیه") return result return wrapper @timing def slow_function(): time.sleep(1) slow_function()

🔹 دکوراتورهای داخلی پرکاربرد

پایتون دکوراتورهای آماده دارد، مثل:

  • staticmethod@

  • classmethod@

  • property@

مثال:

class Person: def __init__(self, name): self._name = name @property def name(self): return self._name

✅ نکات مهم

  • برای حفظ نام و داک‌استرینگ تابع اصلی از functools.wraps استفاده کنید:

    from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
  • دکوراتورها می‌توانند روی توابع عادی، متدهای کلاس و حتی دیگر دکوراتورها استفاده شوند.

  • می‌توان چندین دکوراتور را با هم روی یک تابع قرار داد:

    @decorator1 @decorator2 def func(): ...

💡 تمرین‌های پیشنهادی

  1. دکوراتوری بنویس که هر بار اجرای تابع را لاگ بگیرد (نام تابع و زمان اجرا).

  2. دکوراتوری بساز که خروجی تابع را کش کند تا اگر دوباره با همان ورودی صدا زده شد، از نتیجهٔ ذخیره‌شده استفاده کند.

  3. دکوراتوری بنویس که اگر زمان اجرای تابع بیشتر از 2 ثانیه بود، پیغام هشدار چاپ کند.

دکوراتورها در پایتون یک ویژگی حرفه‌ای و قدرتمند برای جداسازی منطق اصلی کد از منطق جانبی هستند و در فریم‌ورک‌هایی مثل Flask و Django به‌طور گسترده استفاده می‌شوند.