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

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

توابع درجه یک در پایتون

توابع درجه یک به این معنی است که با توابع در پایتون مانند هر شی دیگری رفتار می شود. این بدان معناست که توابع می توانند:

  • به عنوان آرگومان به توابع دیگر منتقل می شود.
  • از توابع دیگر برگشته است.
  • به متغیرها اختصاص داده شده است.

درک بسته شدن

بسته شدن در پایتون به یک تابع اجازه می دهد تا محیطی که در آن ایجاد شده را به خاطر بسپارد. این بدان معناست که تابع داخلی حتی پس از اتمام اجرای تابع خارجی به متغیرهای موجود در محدوده محلی تابع خارجی دسترسی دارد.

بیایید به یک مثال برای درک بسته شدن نگاه کنیم:

def outer_func():
    greet = "Hello!"

    def inner_func():
        print(greet)

    return inner_func

newFunction = outer_func()
newFunction()
newFunction()

در این مثال:

  • ما داریم outer_func که هیچ پارامتری را نمی گیرد.
  • داخل outer_func، یک متغیر محلی وجود دارد greet.
  • یک inner_func درون تعریف شده است outer_func که چاپ می کند greet.
  • outer_func برمی گرداند inner_func.

وقتی زنگ میزنیم outer_func، برمی گردد inner_func اما بلافاصله آن را اجرا نمی کند. تابع برگشتی را به آن اختصاص می دهیم new_function. اکنون، new_function را می توان بعداً فراخوانی کرد و آن را به خاطر می آورد greet متغیر از outer_funcدامنه، چاپ “سلام!” هر بار که تماس گرفته می شود.

این همان بسته شدن است – ما را به یاد می آورد greet متغیر حتی پس از اتمام اجرای تابع خارجی.

روش تغییر بسته ها با پارامترها

اجازه دهید بسته شدن خود را با ارسال یک پارامتر به جای متغیر به the افزایش دهیم outer_func:

def outer_func(greet):
    def inner_func():
        print(greet)
    return inner_func

namasteFunc = outer_func("Namaste!")
howdyFunc = outer_func("Howdy!")

namasteFunc   # Outputs: Namaste!
howdyFunc     # Outputs: Howdy!

اینجا:

  • outer_func حالا یک پارامتر می گیرد greet.
  • این inner_func این را چاپ می کند greet.
  • وقتی زنگ میزنیم outer_func با “Namaste!” و “Howdy!”، عملکردهایی را برمی گرداند که این پیام های خاص را به خاطر می سپارند.

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

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

دکوراتور تابعی است که تابع دیگری را به عنوان آرگومان می گیرد، برخی از قابلیت ها را اضافه می کند و یک تابع جدید را برمی گرداند. این به شما این امکان را می دهد که تابع دیگری را برای گسترش رفتار آن (افزودن برخی عملکردها قبل یا بعد) بدون تغییر کد منبع تابع اصلی، “پیچ بندی” کنید.

بنابراین، این مثال پایانی است که در بالا استفاده کردیم:

def outer_func(greet):
    def inner_func():
        print(greet)
    return inner_func

و این یک مثال دکوراتور است:

def decoratorFunction(func):
    def wrapperFunction():
        return func()
    return wrapperFunction

در اینجا، به جای یک مقدار (مانند greet، ما یک تابع را می پذیریم (func) به عنوان استدلال. در داخل ما wrapperFunction، به جای اینکه فقط یک پیام را چاپ کنیم، این را اجرا می کنیم func و سپس آن را برگردانید.

پیشنهاد می‌کنیم بخوانید:  نحوه غیرفعال کردن هشدارها در پایتون

روش اعمال دکوراتورها به توابع

def decoratorFunction(func):
    def wrapperFunction():
        return func()
    return wrapperFunction

def display():
    print('The display function was called')

decoratedDisplay = decoratorFunction(display)
decoratedDisplay()  # Outputs: The display function was called

در این مثال:

  • ما یک تابع ساده تعریف می کنیم display که پیامی را چاپ می کند.
  • ما اعمال می کنیم decoratorFunction به display، ایجاد یک متغیر جدید decoratedDisplay.
  • وقتی زنگ میزنیم decoratedDisplay()، آن را اجرا می کند wrapperFunction داخل دکوراتور ما، که به نوبه خود زنگ می زند و برمی گرداند display تابع.

روش استفاده از @ نحو برای دکوراتورها

پایتون راه خواناتری برای اعمال دکوراتورها با استفاده از @ سمبل. درک این نحو ساده‌تر است و معمولاً در کد پایتون استفاده می‌شود:

def decoratorFunction(func):
    def wrapperFunction():
        print('Wrapper executed before {}'.format(func.__name__))
        return func()
    return wrapperFunction

@decoratorFunction
def display():
    print('The display function was called')

display()  # Outputs: Wrapper executed before display
           #          The display function was called

اینجا:

  • ما استفاده می کنیم @decoratorFunction بالای display تعریف تابع این قند نحوی برای display = decoratorFunction(display).
  • حالا وقتی زنگ میزنیم display()، به طور خودکار از طریق دکوراتور می رود و ابتدا پیام اضافی را چاپ می کند.

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

اگر تابع اصلی ما آرگومان‌هایی داشته باشد، دکوراتوری که تاکنون نوشته‌ایم کار نخواهد کرد. برای مثال تابع زیر را در نظر بگیرید:

def display_info(name, age):
    print('display_info was called with ({}, {})'.format(name, age))

display_info('Abdul Kalam', 83)  # Outputs: display_info was called with (Abdul Kalam, 83)

اگر سعی کنیم دکوراتور فعلی خود را اعمال کنیم display_info، یک خطا ایجاد می کند زیرا wrapperFunction هیچ آرگومان نمی گیرد اما تابع اصلی دو آرگومان را انتظار دارد.

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

ما می توانیم دکوراتور خود را طوری تغییر دهیم که با استفاده از آن، هر تعداد آرگومان موقعیتی و کلیدواژه را بپذیرد *args و **kwargs.

def decoratorFunction(func):
    def wrapperFunction(*args, **kwargs):
        print('Wrapper executed before {}'.format(func.__name__))
        return func(*args, **kwargs)
    return wrapperFunction

@decoratorFunction
def display():
    print('The display function was called')

@decoratorFunction
def display_info(name, age):
    print('display_info was called with ({}, {})'.format(name, age))

display_info('Abdul Kalam', 83)  # Outputs: Wrapper executed before display_info
                           #          display_info was called with (Abdul Kalam, 83)
display()                  # Outputs: Wrapper executed before display
                           #          The display function was called

در این دکوراتور به روز شده:

  • wrapperFunction اکنون هر تعداد موقعیت (*args) و آرگومان های کلمه کلیدی (**kwargs).
  • این استدلال ها به func هنگامی که آن را در داخل نامیده می شود wrapperFunction.
  • این تنظیم باعث می شود دکوراتور ما به اندازه کافی انعطاف پذیر باشد تا بتواند هر عملکردی را بدون توجه به پارامترهای آن انجام دهد.

روش استفاده از کلاس ها به عنوان دکوراتور

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

در اینجا روش تبدیل یک دکوراتور مبتنی بر عملکرد به یک دکوراتور کلاس محور آورده شده است:

پیشنهاد می‌کنیم بخوانید:  متد ()String.replace پایتون

ابتدا با دکوراتور مبتنی بر عملکرد اصلی شروع می کنیم:

def decoratorFunction(func):
    def wrapperFunction(*args, **kwargs):
        print('Wrapper executed before calling {}'.format(func.__name__))
        return func(*args, **kwargs)
    return wrapperFunction

بعد، کلاسی ایجاد می کنیم که این عملکرد را تقلید می کند:

مرحله 1: کلاس را تعریف کنید

یک کلاس جدید به نام تعریف می کنیم DecoratorClass. این کلاس به دکوراسیون رسیدگی خواهد کرد process.

class DecoratorClass:

مرحله 2: __init__ روش

این __init__ متد یک متد خاص در کلاس های پایتون است. زمانی فراخوانی می شود که یک نمونه (ابجکت) از کلاس ایجاد شود. این روش شی را مقداردهی اولیه می کند.

  • عبور می کنیم func به عنوان یک استدلال برای __init__ روش.
  • درون __init__ روش، ما ذخیره می کنیم func در یک متغیر نمونه self.func.
class DecoratorClass:
    def __init__(self, func):
        self.func = func

مرحله 3: __call__ روش

این __call__ روش یک روش خاص دیگر است. این اجازه می دهد تا یک نمونه از کلاس به عنوان یک تابع فراخوانی شود.

  • این __call__ روش می گیرد *args و **kwargs برای رسیدگی به هر تعداد آرگومان موقعیتی و کلید واژه.
  • داخل __call__، ما print یک پیام و سپس تابع اصلی را با آرگومان های آن فراخوانی کنید.
class DecoratorClass:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('Executing wrapper before {}'.format(self.func.__name__))
        return self.func(*args, **kwargs)

با استفاده از دکوراتور کلاس محور

سپس از @ نحو برای اعمال دکوراتور مبتنی بر کلاس به توابع، درست مانند کاری که با دکوراتور مبتنی بر تابع انجام دادیم.

@DecoratorClass
def display():
    print('display function executed')

@DecoratorClass
def display_info(name, age):
    print('display_info function executed with arguments ({}, {})'.format(name, age))

اجرای توابع تزئین شده

وقتی توابع تزئین شده را فراخوانی می کنیم __call__ روش از DecoratorClass اجرا می شود:

display_info('Abdul Kalam', 83)
display()

در اینجا مثال کاملی از دکوراتورهای کلاسی آورده شده است:

class DecoratorClass:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('Executing wrapper before {}'.format(self.func.__name__))
        return self.func(*args, **kwargs)

@DecoratorClass
def display():
    print('display function executed')

@DecoratorClass
def display_info(name, age):
    print('display_info function executed with arguments ({}, {})'.format(name, age))

display_info('Abdul Kalam', 83)
display()

در این دکوراتور کلاس محور:

  • این __init__ متد تابع اصلی را به نمونه ای از کلاس پیوند می دهد.
  • این __call__ روش اجازه می دهد تا یک نمونه از DecoratorClass به عنوان یک تابع فراخوانی شود. پیامی را چاپ می کند و تابع اصلی را با هر آرگومان فراخوانی می کند.
  • تزئین می کنیم display و display_info با @DecoratorClass.
  • چه زمانی display_info('Abdul Kalam', 83) نامیده می شود، آن را اجرا می کند __call__ روش از DecoratorClass، پیام را چاپ می کند و سپس اجرا می کند display_info.
  • چه زمانی display() نامیده می شود، آن را اجرا می کند __call__ روش از DecoratorClass، پیام را چاپ می کند و سپس اجرا می کند display.

هر دو دکوراتور مبتنی بر عملکرد و کلاس مبتنی بر عملکرد یکسانی را ارائه می دهند. انتخاب بین آنها بستگی دارد روی ترجیح شخصی و پیچیدگی منطق دکوراتور.

نتیجه

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

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

  • دکوراتورها برای گسترش عملکرد عملکردها قدرتمند هستند.
  • آنها را می توان با استفاده از توابع یا کلاس ها پیاده سازی کرد.
  • این @decorator نحو روشی تمیزتر و خواناتر برای اعمال تزئینات است.
  • آنها با انتزاع کردن عملکردهای رایج به حفظ کد شما کمک می کنند تا DRY (خودتان را تکرار نکنید).