در برنامه نویسی decorator یک دیزاین پترن است که امکانات اضافی به یک object اضافه می کند. در پایتون دکوراتورها ابزاری هستند که امکانات اضافی را بدون تغییر تابع به توابع اضافه می کنند.
دکوراتورها یکی از ابزارهای قدرتمند در python هستند که یک تابع را به عنوان argument می پذیرند و به کمک آنها می توان رفتار تابع argument را تغییر داد بدون اینکه تابع اصلی تغییر کند. به عبارت دیگر دکوراتورها این امکان را به برنامه نویس می دهند که رفتار توابع را به طور موقتی تغییر دهند. دکوراتورها با استفاده از decorator@ روی توابع اعمال می شوند.
به طور کلی دکوراتور ها به صورت زیر تعریف می شوند:
def mydecoratorfunction(some_function): # decorator function def inner_function(): # write code to extend the behavior of some_function() some_function() # call some_function # write code to extend the behavior of some_function() return inner_function # return a wrapper function
دکوراتورها روش ساده برای اجرای توابع higher-order هستند. توابع higher-order توابعی هستند که یک یا چند تابع را به عنوان آرگومان ورودی دریافت می کنند و یا یک تابع را return می کنند.
در دکوراتور، function ها به عنوان ورودی به تابع دیگر پاس داده می شود و سپس داخل تابع wrapper صدا زده می شود. هدف اصلی از نوشتن دکوراتورها جلوگیری از نوشتن کدهای تکراری است به این صورت که توابعی را یکبار نوشته و از آنها برای متدهای مختلف استفاده می کنیم. مانند authorization که با استفاده از @ در بالای متد هایی که نیاز به اعتبار سنجی دارند قرار داده می شود.
توابع در پایتون object هستند، بنابراین می توانند refrence داده شوند یا به عنوان متغیر به تابع دیگر ارسال شوند و یا توسط تابع دیگر return شوند. همچنین توابع می توانند درون یکدیگر اجرا شوند و متغیر های خود را به توابع درونی پاس دهند.
مثال زیر را در نظر بگیرید که تابعی را در ورودی دریافت می کند و زمان اجرای آن را محاسبه می کند:
import time import math def calculate_time(func): def inner(*args, **kwargs): begin = time.time() func(*args, **kwargs) end = time.time() print("Total time taken in : ", func.__name__, end - begin) return inner
حال از تابع calculate_time با استفاده از @ می توان به صورت دکوراتور برای توابع دیگر استفاده کرد:
@calculate_time def factorial(num): time.sleep(2) print(math.factorial(num))
خروجی کد بالا به صورت زیر خواهد بود:
>>>factorial(10) 3628800 Total time taken in : factorial 2.0061802864074707
تشریح:
در کد بالا تابع factorial(num) به عنوان ورودی به دکوراتور calculate_time ارسال می شود و در این دکوراتور اجرا شده و تابع inner(*args, **kwargs) زمان اجرای آن را محاسبه می کند.
می توان دکوراتورها را به صورت زنجیره ای از رکوراتورها به صورت زیر استفاده کرد:
def decor1(func): def inner(): x = func() return x * x return inner def decor(func): def inner(): x = func() return 2 * x return inner @decor1 @decor def num(): return 10
print(num())
خروجی کد بالا عدد 400 خواهد بود.
import functools def decorator(func): @functools.wraps(func) def wrapper_decorator(*args, **kwargs): value = func(*args, **kwargs) return value return wrapper_decorator
def decorator(*args, **kwargs): print("Inside decorator") def inner(func): print("Inside inner function") print("I like", kwargs['like']) func() return inner
@decorator(like = "geeksforgeeks") def func(): print("Inside actual function")
با اجرای کد بالا خروجی زیر را خواهیم داشت:
Inside decorator Inside inner function I like geeksforgeeks Inside actual function