دکوریتورها در پایتون: مفاهیم متوسط

دکوریتورها در پایتون: مفاهیم متوسط

دکوریتورها یکی از ویژگی‌های قدرتمند و انعطاف‌پذیر پایتون هستند که به شما امکان می‌دهند تا رفتار توابع را بدون تغییر در کد اصلی آن‌ها، تغییر دهید. این مفهوم در ابتدا ممکن است کمی پیچیده به نظر برسد، اما با درک صحیح، می‌تواند به شما در نوشتن کدهای تمیزتر، قابل‌نگهداری‌تر و با قابلیت استفاده مجدد کمک کند. این مقاله به بررسی عمیق دکوریتورها در پایتون می‌پردازد و کاربردهای مختلف آن‌ها را شرح می‌دهد.

مقدمه‌ای بر دکوریتورها

به طور خلاصه، یک دکوریتور یک تابع است که تابع دیگری را به عنوان ورودی می‌گیرد و یک تابع جدید را به عنوان خروجی برمی‌گرداند. این تابع جدید، معمولاً رفتار تابع اصلی را با افزودن یا تغییر برخی ویژگی‌ها، تغییر می‌دهد. دکوریتورها از ویژگی‌های درجه اول توابع در پایتون استفاده می‌کنند، به این معنی که توابع می‌توانند به عنوان آرگومان به توابع دیگر ارسال شوند و از توابع برگردانده شوند.

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

استفاده از دکوریتورها مزایای متعددی دارد:

  • قابلیت استفاده مجدد: دکوریتورها را می‌توان برای افزودن رفتار مشابه به چندین تابع استفاده کرد، بدون اینکه نیاز به تکرار کد باشد.
  • خوانایی کد: دکوریتورها می‌توانند کد را تمیزتر و خواناتر کنند، زیرا منطق اضافی را از کد اصلی تابع جدا می‌کنند.
  • تغییرپذیری: دکوریتورها به شما امکان می‌دهند تا رفتار توابع را بدون تغییر در کد اصلی آن‌ها، تغییر دهید.
  • اصل تک مسئولیتی (Single Responsibility Principle): دکوریتورها به شما کمک می‌کنند تا اصل تک مسئولیتی را رعایت کنید، زیرا هر تابع فقط یک کار را انجام می‌دهد.

نحوه تعریف و استفاده از دکوریتورها

برای تعریف یک دکوریتور، ابتدا یک تابع تعریف می‌کنیم که تابع دیگری را به عنوان ورودی می‌گیرد. این تابع، یک تابع جدید را تعریف می‌کند که رفتار تابع اصلی را تغییر می‌دهد. سپس، تابع اصلی را در تابع جدید فراخوانی می‌کنیم و تابع جدید را برمی‌گردانیم. برای استفاده از یک دکوریتور، از علامت @ قبل از تعریف تابع استفاده می‌کنیم و نام دکوریتور را بعد از آن می‌نویسیم.

مثال ساده

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

def my_decorator(func):
    def wrapper():
        print("قبل از اجرای تابع")
        func()
        print("بعد از اجرای تابع")
    return wrapper

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

say_hello()

در این مثال، my_decorator یک دکوریتور است که تابع say_hello را به عنوان ورودی می‌گیرد. تابع wrapper یک تابع جدید است که قبل از اجرای say_hello، پیام “قبل از اجرای تابع” را چاپ می‌کند و بعد از اجرای آن، پیام “بعد از اجرای تابع” را چاپ می‌کند. با استفاده از علامت @my_decorator، تابع say_hello با دکوریتور my_decorator تزئین می‌شود. هنگامی که say_hello() را فراخوانی می‌کنیم، در واقع تابع wrapper را فراخوانی می‌کنیم که رفتار say_hello را تغییر می‌دهد.

دکوریتورها با آرگومان

تاکنون دکوریتوری را بررسی کردیم که هیچ آرگومانی دریافت نمی‌کرد. اما اگر تابع اصلی آرگومان دریافت کند، چگونه می‌توانیم از دکوریتور استفاده کنیم؟ برای این کار، باید تابع wrapper را طوری تعریف کنیم که آرگومان‌ها را دریافت کند و آن‌ها را به تابع اصلی ارسال کند.

مثال با آرگومان

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

result = add(5, 3)
print(result)

در این مثال، تابع wrapper از *args و **kwargs برای دریافت آرگومان‌های موقعیتی و کلمه‌ای استفاده می‌کند. سپس، این آرگومان‌ها را به تابع اصلی add ارسال می‌کند. همچنین، مقدار بازگشتی تابع اصلی را ذخیره می‌کند و آن را برمی‌گرداند.

دکوریتورها با پارامترها

گاهی اوقات، ممکن است بخواهیم دکوریتورهایی تعریف کنیم که پارامترهای خاص خود را داشته باشند. برای این کار، باید یک تابع بیرونی تعریف کنیم که پارامترهای دکوریتور را دریافت کند و یک دکوریتور را برگرداند. این دکوریتور، تابع اصلی را به عنوان ورودی می‌گیرد و تابع wrapper را برمی‌گرداند.

مثال با پارامتر

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"سلام {name}!")

greet("علی")

در این مثال، تابع repeat یک پارامتر به نام num_times دریافت می‌کند که تعداد دفعاتی را که تابع اصلی باید تکرار شود، مشخص می‌کند. تابع decorator_repeat یک دکوریتور است که تابع اصلی را به عنوان ورودی می‌گیرد. تابع wrapper تابع اصلی را به تعداد دفعات مشخص شده تکرار می‌کند. با استفاده از @repeat(num_times=3)، تابع greet با دکوریتور repeat تزئین می‌شود و پارامتر num_times برابر با 3 تنظیم می‌شود.

کاربردهای پیشرفته دکوریتورها

دکوریتورها کاربردهای بسیار متنوعی دارند. در اینجا چند نمونه از کاربردهای پیشرفته آن‌ها آورده شده است:

  • ثبت (Logging): دکوریتورها می‌توانند برای ثبت اطلاعات مربوط به اجرای توابع، مانند زمان شروع و پایان، آرگومان‌ها و مقدار بازگشتی، استفاده شوند.
  • اندازه‌گیری زمان اجرا (Timing): دکوریتورها می‌توانند برای اندازه‌گیری زمان اجرای توابع استفاده شوند.
  • کنترل دسترسی (Access Control): دکوریتورها می‌توانند برای کنترل دسترسی به توابع استفاده شوند، به عنوان مثال، فقط کاربران دارای مجوز خاص می‌توانند یک تابع را فراخوانی کنند.
  • اعتبارسنجی ورودی (Input Validation): دکوریتورها می‌توانند برای اعتبارسنجی ورودی‌های توابع استفاده شوند.
  • کش کردن (Caching): دکوریتورها می‌توانند برای کش کردن نتایج توابع استفاده شوند، به طوری که اگر تابع با همان آرگومان‌ها دوباره فراخوانی شود، نتیجه کش شده برگردانده شود.

نتیجه‌گیری

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

بدون دیدگاه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *