دکوراتورها چه قابلیت‌هایی در اختیار برنامه‌نویسان قرار می‌دهند؟
دکوراتورها در پایتون و نحوه پیاده‌سازی آن‌ها
دکوراتورها (Decorators) در پایتون، نوعی از توابع هستند که می‌توانند برای تغییر رفتار یک تابع یا کلاس، به کار رود. این تغییرات می‌توانند شامل افزودن عملکرد به یک تابع، تغییر ورودی یا خروجی یک تابع، و یا تغییر رفتار یک کلاس باشند. دکوراتورها با استفاده از کاراکتر @ به تابع یا کلاس اعمال می‌شوند. یک تابع دکوره شده، قابلیت اجرای دوباره با نام متفاوت را دارد. این ویژگی باعث می‌شود که دکوراتورها برای تغییر رفتار تعداد زیادی از توابع در یک برنامه مفید باشند.

در پایتون، دکوراتورهای مفید مختلفی وجود دارند که می‌توانند برای تغییر رفتار توابع و کلاس‌ها به کار گرفته شوند. برخی از این دکوراتورها عبارتند از: @staticmethod، @classmethod، @property، @abstractmethod، @asyncio.coroutine و...

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

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

همچنین، دکوراتورها می‌توانند برای تعریف توابع خودکار (Automatic functions) مفید باشند. به عنوان مثال، می‌توان از دکوراتور `@property` برای تعریف یک ویژگی محاسبه‌ای (Computed property) استفاده کرد که با فراخوانی آن، مقداری محاسبه شود و به عنوان خروجی بازگردانده شود.

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

به طور کلی، دکوراتورها، برای تغییر رفتار توابع و کلاس‌ها در پایتون به کار می‌روند و می‌توانند برای تعریف توابع خودکار، تعیین نوع ورودی‌ها و خروجی‌ها، بررسی ورودی‌ها و خروجی‌ها و همچنین تعیین سطح دسترسی توابع استفاده شوند.

دکوراتورها چه قابلیت‌هایی در اختیار برنامه‌نویسان قرار می‌دهند؟

دکوراتورها قابلیت‌های کاربردی مختلف و سطح بالایی در اختیار برنامه‌نویسان قرار می‌دهند که برخی از آن‌ها به شرح زیر هستند:

  • تغییر رفتار توابع: دکوراتورها به برنامه‌نویسان امکان تغییر رفتار توابع را می‌دهند. به عنوان مثال، با استفاده از دکوراتور `@staticmethod` می‌توان تابع استاتیک تعریف کرد که در فراخوانی‌های آن، مانند یک تابع معمولی، بدون نیاز به ایجاد نمونه از کلاس، فراخوانی شود.
  • تعریف ویژگی‌های خودکار: دکوراتورها به برنامه‌نویسان امکان تعریف ویژگی‌های خودکار (Automatic properties) را می‌دهند. به عنوان مثال، با استفاده از دکوراتور `@property` می‌توان ویژگی محاسبه‌ای (Computed property) تعریف کرد که با فراخوانی آن، مقداری محاسبه شود و به عنوان خروجی بازگردانده شود.
  • تعیین نوع ورودی‌ها و خروجی‌ها: دکوراتورها به برنامه‌نویسان امکان تعیین نوع ورودی‌ها و خروجی‌های توابع را می‌دهند. به عنوان مثال، با استفاده از دکوراتور `@typing` می‌توان نوع ورودی‌ها و خروجی‌های یک تابع را مشخص کرد.
  • بررسی ورودی‌ها و خروجی‌ها: دکوراتورها به برنامه‌نویسان امکان بررسی ورودی‌ها و خروجی‌های توابع را می‌دهند. به عنوان مثال، با استفاده از دکوراتورهای `@check_arguments` و `@check_return` می‌توان ورودی‌ها و خروجی‌های یک تابع را بررسی کرد.
  • تعیین سطح دسترسی توابع: دکوراتورها به برنامه نویسان امکان تعیین سطح دسترسی توابع را می‌دهند. به عنوان مثال، با استفاده از دکوراتور `@staticmethod` می‌توان تابع استاتیکی تعریف کرد که بدون نیاز به ساخت نمونه از کلاس، فراخوانی شود.

به طور کلی، دکوراتورها به برنامه‌نویسان امکان تغییر رفتار توابع و کلاس‌ها، تعریف ویژگی‌های خودکار، تعیین نوع ورودی‌ها و خروجی‌ها، بررسی ورودی‌ها و خروجی‌ها و تعیین سطح دسترسی توابع را می‌دهند.

مثالی از نحوه استفاده از دکوراتورها در پایتون

یک مثال از نحوه استفاده از دکوراتورها در پایتون، استفاده از دکوراتور `@timing` است که زمان اجرای یک تابع را محاسبه می‌کند. برای این منظور، می‌توان دکوراتور `@timing` را به تابع اعمال کرد. به طور مثال، کد زیر نمونه‌ای از این استفاده را نشان می‌دهد:

import time

def timing_decorator(func):

    def wrapper(*args, **kwargs):

        start_time = time.time()

        result = func(*args, **kwargs)

        end_time = time.time()

        print(f"Elapsed time: {end_time - start_time}")

        return result

    return wrapper

@timing_decorator

def my_function():

    time.sleep(2)

my_function()

در این مثال، دکوراتور `@timing_decorator` تعریف شده و به تابع `my_function` اعمال شده است. این دکوراتور، زمان شروع اجرای تابع را ثبت کرده و پس از اجرای تابع، زمان پایان اجرای آن را ثبت کرده و زمان گذشته بین این دو زمان را محاسبه می‌کند. سپس، این زمان را به عنوان خروجی چاپ می‌کند.

در این مثال، با استفاده از دکوراتور `@timing_decorator`، نیازی به تغییر کد تابع `my_function` نبوده و با اعمال این دکوراتور، به راحتی می‌توان زمان اجرای آن را محاسبه کرد.

آیا دکوراتورهای دیگری هم در پایتون وجود دارند؟

بله، در پایتون دکوراتورهای مفید و کاربردی مختلفی وجود دارند. در ادامه، به برخی از این دکوراتورها اشاره می‌کنم:

  • `@staticmethod`: این دکوراتور برای تعریف توابع استاتیک استفاده می‌شود که به صورت مستقل از نمونه‌ای از کلاس، قابل فراخوانی هستند.
  •  `@classmethod`: این دکوراتور برای تعریف توابع کلاس استفاده می‌شود که برای انجام عملیاتی مربوط به کلاس، مفید هستند.
  • `@property`: این دکوراتور برای تعریف ویژگی‌های خودکار (Computed properties) استفاده می‌شود که با فراخوانی، مقداری را محاسبه کرده و به عنوان خروجی بازگردانده می‌شوند.
  • `@abstractmethod`: این دکوراتور برای تعریف توابع انتزاعی (Abstract methods) در کلاس‌ها استفاده می‌شود که باید در زیرکلاس‌ها پیاده‌سازی شوند.
  • `@staticmethod`: این دکوراتور برای تعریف توابع استاتیک استفاده می‌شود که به صورت مستقل از نمونه‌ای از کلاس، قابل فراخوانی هستند.
  • `@typing`: این دکوراتور برای تعیین نوع ورودی‌ها و خروجی‌های توابع استفاده می‌شود.
  • `@wraps`: این دکوراتور برای حفظ اطلاعاتی مانند نام و مستندات تابع در داخل یک تابع دیگر استفاده می‌شود.
  • `@lru_cache`: این دکوراتور برای حفظ نتایج حسابی تابع و جلوگیری از محاسبه مجدد آن‌ها در فراخوانی‌های بعدی استفاده می‌شود.
  • `@asyncio`: این دکوراتور برای تعریف توابع ناهمگام (Asynchronous functions) استفاده می‌شود که در برنامه‌های کوچک و بزرگ کاربرد فراوانی دارد.
  • دکوراتور `@retry`: این دکوراتور برای تکرار اجرای یک تابع در صورت بروز خطا استفاده می‌شود. با استفاده از این دکوراتور، می‌توان اجرای تابع را برای تعداد مشخصی بار تکرار کرد تا در صورت بروز خطا، تلاش مجدد شود.
  • دکوراتور `@cache`: این دکوراتور برای حفظ نتایج حسابی تابع و جلوگیری از محاسبه مجدد آن‌ها در فراخوانی‌های بعدی استفاده می‌شود. با استفاده از این دکوراتور، می‌توان کارایی برنامه را بهبود داد و زمان اجرای آن را کاهش داد.
  • دکوراتور `@login_required`: این دکوراتور برای محافظت از محتوایی که نیاز به ورود کاربر دارد، استفاده می‌شود. با استفاده از این دکوراتور، می‌توان اجازه دسترسی به صفحات مشخصی را فقط به کاربرانی که وارد سایت شده‌اند، داد.
  • دکوراتور `@validate_args`: این دکوراتور برای تأیید صحت ورودی‌های تابع استفاده می‌شود. با استفاده از این دکوراتور، می‌توان تأیید کرد که ورودی‌های تابع درست و کامل هستند و اگر مشکلی وجود دارد، خطا می‌دهد.
  • دکوراتور `@synchronized`: این دکوراتور برای قفل کردن دسترسی به یک قسمت از کد استفاده می‌شود. با استفاده از این دکوراتور، می‌توان از همروندی در کد جلوگیری کرد و اطمینان حاصل کرد که تنها یک نخ به یک بخش از کد دسترسی دارد.
  • دکوراتور `@log`: این دکوراتور برای ثبت لاگ‌های اجرای کد استفاده می‌شود. با استفاده از این دکوراتور، می‌توان فعالیت‌های انجام شده در برنامه را ثبت و در صورت نیاز، بررسی کرد.
  • دکوراتور `@suppress`: این دکوراتور برای ساکت کردن خطاها استفاده می‌شود. با استفاده از این دکوراتور، می‌توان از این خطاها صرف‌نظر کرد و ادامه اجرای برنامه را از سطح خطا به سطح اطلاعاتی بالاتر برد.

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

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

بله، در پایتون می‌توان دکوراتورها را با هم ترکیب کرد. این کار به کاربردی‌تر شدن دکوراتورها و گسترش قابلیت‌های آن‌ها کمک می‌کند.

برای ترکیب دو دکوراتور، می‌توان آن‌ها را به صورت پشت سر هم قرار داد. به عنوان مثال، اگر بخواهیم یک تابع را به صورت کلاسی با دکوراتور `@staticmethod` تعریف کنیم و همچنین از دکوراتور `@cache` برای حفظ نتایج حسابی استفاده کنیم، می‌توانیم به شکل زیر عمل کنیم:

class MyClass:

    @staticmethod

    @cache

    def my_static_method(x):

        # این تابع به عنوان یک تابع استاتیک تعریف شده و در صورت فراخوانی، با استفاده از دکوراتور cache، نتیجه حسابی آن ذخیره می‌شود.

        return x ** 2

با این کار، تابع `my_static_method` به عنوان یک تابع استاتیک تعریف شده و در صورت فراخوانی، نتیجه حسابی آن در صورت وجود در حافظه ذخیره شده و در فراخوانی‌های بعدی، دوباره محاسبه نمی‌شود.

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

چگونه دکوراتورها را با توابع ترکیب کنیم؟

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

برای مثال، فرض کنید یک تابع داریم که یک عدد را به عنوان ورودی دریافت می‌کند و خروجی آن، مجموع اعداد فرد کوچکتر از آن عدد است. حال می‌خواهیم این تابع را به صورت یک تابع با دو دکوراتور `@memoize` و `@timed` تعریف کنیم تا در صورت فراخوانی‌های بعدی، نتیجه حسابی آن در حافظه ذخیره شود و زمان اجرای آن نیز با دقت ثبت شود. می‌توانیم به شکل زیر عمل کنیم:

import time

def memoize(func):

    cache = {}

    def wrapper(*args):

        if args in cache:

            return cache[args]

        else:

            result = func(*args)

            cache[args] = result

            return result

    return wrapper

def timed(func):

    def wrapper(*args):

        start = time.time()

        result = func(*args)

        end = time.time()

        print(f"Elapsed time: {end-start:.6f} seconds")

        return result

    return wrapper

@memoize

@timed

def sum_of_odd_numbers(n):

    return sum(filter(lambda x: x % 2 == 1, range(n)))

print(sum_of_odd_numbers(1000000))

print(sum_of_odd_numbers(1000000))

در این کد، تابع `memoize` برای حفظ نتایج حسابی تابع و تابع `timed` برای ثبت زمان اجرای تابع در هر فراخوانی استفاده شده‌اند. سپس با استفاده از دکوراتورهای `memoize` و `timed`، تابع `sum_of_odd_numbers` تعریف شده است. هنگام فراخوانی این تابع، نتیجه حسابی آن در صورت وجود در حافظه ذخیره شده و در فراخوانی‌های بعدی، دوباره محاسبه نمی‌شود. همچنین، زمان اجرای تابع در هر فراخوانی با استفاده از دکوراتور `timed` ثبت و چاپ می‌شود. به این ترتیب، با ترکیب دکوراتورها با توابع دیگر، می‌توان قابلیت‌های بیشتری برای توابع خود اضافه کرد.

چگونه دکوراتورها را با کلاس‌ها ترکیب کنیم؟

در پایتون می‌توان دکوراتورها را با کلاس‌ها نیز ترکیب کرد. در واقع دکوراتورهایی که برای توابع تعریف می‌شوند، به صورت پشتیبانی شده‌ترین حالت دکوراتورها هستند ولی دکوراتورها می‌توانند برای کلاس‌ها نیز تعریف شوند. به عنوان مثال، فرض کنید یک کلاس داریم که مشخصات یک شخص را در خود ذخیره می‌کند و می‌خواهیم از دو دکوراتور `@property` و `@classmethod` برای تعریف ویژگی‌های مربوط به کلاس استفاده کنیم. می‌توانیم به شکل زیر عمل کنیم:

class Person:

    def __init__(self, name, age):

        self._name = name

        self._age = age

    @property

    def name(self):

        return self._name

    @property

    def age(self):

        return self._age

    @classmethod

    def from_birth_year(cls, name, birth_year):

        age = cls.calculate_age(birth_year)

        return cls(name, age)

    @staticmethod

    def calculate_age(birth_year):

        return 2023 - birth_year

در این کد، `@property` برای تعریف ویژگی‌های `name` و `age` به کار رفته است. همچنین، `@classmethod` برای تعریف تابع `from_birth_year` به کار رفته است که با گرفتن سال تولد یک شخص، نام و سن آن را به صورت یک شیء از کلاس باز می‌گرداند. همچنین، تابع `calculate_age` به عنوان یک تابع استاتیک تعریف شده و برای محاسبه سن از آن استفاده شده است. به این ترتیب، با ترکیب دکوراتورها با کلاس‌ها، می‌توان قابلیت‌های بیشتری برای کلاس‌های خود اضافه کرد.

آیا می‌توان تابع‌های دیگری را به عنوان دکوراتور برای کلاس‌ها استفاده کرد؟

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

برای مثال، فرض کنید یک کلاس داریم که یک عدد را به عنوان ورودی دریافت می‌کند و خروجی آن، مجموع اعداد فرد کوچکتر از آن عدد است. حال می‌خواهیم این کلاس را به صورت یک کلاس با دو دکوراتور `@memoize` و `@timed` تعریف کنیم تا در صورت فراخوانی‌های بعدی، نتیجه حسابی آن در حافظه ذخیره شود و زمان اجرای آن نیز با دقت ثبت شود. می‌توانیم به شکل زیر عمل کنیم:

import time

def memoize(cls):

    cache = {}

    init = cls.__init__

    def wrapper(self, n):

        if n in cache:

            return cache[n]

        else:

            instance = cls.__new__(cls)

            init(instance, n)

            result = instance.sum_of_odd_numbers()

            cache[n] = result

            return result

    cls.__init__ = wrapper

    return cls

def timed(cls):

    old_sum_of_odd_numbers = cls.sum_of_odd_numbers

    def new_sum_of_odd_numbers(self):

        start = time.time()

        result = old_sum_of_odd_numbers(self)

        end = time.time()

        print(f"Elapsed time: {end-start:.6f} seconds")

        return result

    cls.sum_of_odd_numbers = new_sum_of_odd_numbers

    return cls

@memoize

@timed

class SumOfOddNumbers:

    def __init__(self, n):

        self.n = n

    def sum_of_odd_numbers(self):

        return sum(filter(lambda x: x % 2 == 1, range(self.n)))

s = SumOfOddNumbers(1000000)

print(s.sum_of_odd_numbers())

print(s.sum_of_odd_numbers())

در این کد، تابع `memoize` برای حفظ نتایج حسابی تابع و تابع `timed` برای ثبت زمان اجرای تابع در هر فراخوانی استفاده شده‌اند. سپس با استفاده از دکوراتورهای `memoize` و `timed`، کلاس `SumOfOddNumbers` تعریف شده است. هنگام فراخوانی تابع `sum_of_odd_numbers` در این کلاس، نتیجه حسابی آن در صورت وجود در حافظه ذخیره شده و در فراخوانی‌های بعدی، دوباره محاسبه نمی‌شود. همچنین، زمان اجرای تابع در هر فراخوانی با استفاده از دکوراتور `timed` ثبت و چاپ می‌شود.

به این ترتیب، با ترکیب توابع دیگر با کلاس‌ها به عنوان دکوراتور، می‌توان قابلیت‌هایبیشتری به کلاس‌ها اضافه کرد و آن‌ها را به شکلی هوشمندانه‌تر و کارآمدتری طراحی کرد.

آیا دکوراتورهای دیگری برای تعریف ویژگی‌های خودکار وجود دارند؟

بله، در پایتون دکوراتورهای دیگری برای تعریف ویژگی‌های خودکار وجود دارند. برای مثال، دکوراتور `@property` برای تعریف ویژگی‌های خواندنی، `@classmethod` برای تعریف متد کلاسی و `@staticmethod` برای تعریف تابع استاتیک به کار می‌روند. همچنین، دکوراتور `@setter` و `@deleter` برای تعریف ویژگی‌های قابل تغییر و حذف، به کار می‌روند.

به عنوان مثال، فرض کنید می‌خواهیم یک کلاس برای نمایش تاریخ تولد یک شخص تعریف کنیم. ابتدا می‌توانیم یک ویژگی خواندنی برای تعیین تاریخ تولد اضافه کنیم و سپس دو ویژگی قابل تغییر و حذف برای تغییر و حذف تاریخ تولد تعریف کنیم. می‌توانیم به شکل زیر عمل کنیم:

class Birthday:

    def __init__(self, year, month, day):

        self._year = year

        self._month = month

        self._day = day

    @property

    def year(self):

        return self._year

    @property

    def month(self):

        return self._month

    @property

    def day(self):

        return self._day

    @year.setter

    def year(self, value):

        self._year = value

    @month.setter

    def month(self, value):

        self._month = value

    @day.setter

    def day(self, value):

        self._day = value

    @year.deleter

    def year(self):

        del self._year

    @month.deleter

    def month(self):

        del self._month

    @day.deleter

    def day(self):

        del self._day

    def __str__(self):

        return f"{self._year}/{self._month}/{self._day}"

در این کد، از دکوراتور `@property` برای تعریف ویژگی‌های خواندنی `year`، `month` و `day` استفاده شده است. همچنین، از دکوراتور `@setter` برای تعریف ویژگی‌های قابل تغییر `year`، `month` و `day` و از دکوراتور `@deleter` برای تعریف ویژگی‌های قابل حذف `year`، `month` و `day` استفاده شده است. در نهایت، با تعریف تابع `__str__`، تاریخ تولد به شکل رشته‌ای به صورت سال/ماه/روز چاپ می‌شود.

به این ترتیب، با استفاده از دکوراتورهای `@property`، `@setter` و `@deleter`، می‌توانیم ویژگی‌های خودکار را به کلاس‌ها اضافه کرده و آن‌ها را به شکلی هوشمندانه‌تر و کارآمدتری طراحی کنیم.

ماهنامه شبکه را از کجا تهیه کنیم؟
ماهنامه شبکه را می‌توانید از کتابخانه‌های عمومی سراسر کشور و نیز از دکه‌های روزنامه‌فروشی تهیه نمائید.

ثبت اشتراک نسخه کاغذی ماهنامه شبکه     
ثبت اشتراک نسخه آنلاین

 

کتاب الکترونیک +Network راهنمای شبکه‌ها

  • برای دانلود تنها کتاب کامل ترجمه فارسی +Network  اینجا  کلیک کنید.

کتاب الکترونیک دوره مقدماتی آموزش پایتون

  • اگر قصد یادگیری برنامه‌نویسی را دارید ولی هیچ پیش‌زمینه‌ای ندارید اینجا کلیک کنید.

ایسوس

نظر شما چیست؟