دکوراتورها در پایتون ابزاری قدرتمند و منعطف برای تغییر یا گسترش رفتار توابع و متدها بدون تغییر در کد اصلی آنها هستند. دکوراتورها در پایتون در واقع توابعی هستند که به دیگر توابع به عنوان ورودی داده میشوند و میتوانند رفتار آنها را پیش یا پس از اجرا تغییر دهند. این ویژگی در پایتون به توسعهدهندگان این امکان را میدهد که از کدهای تکراری جلوگیری کنند و به راحتی قابلیتهای اضافی مانند اعتبارسنجی ورودی، اجرای پیشپردازش یا پسپردازش، و مدیریت دسترسی را به توابع و متدها اضافه کنند. استفاده از دکوراتورها، کدنویسی را خواناتر، قابل نگهداریتر و منعطفتر میکند.
مقدمه
دکوراتورها در پایتون ابزار بسیار قدرتمند و مفیدی در پایتون هستند زیرا به برنامهنویسان این امکان را میدهند که رفتار یک تابع یا کلاس را تغییر دهند. دکوراتورها به ما اجازه میدهند که یک تابع دیگر را در بر بگیریم تا رفتار تابع در بر گرفته شده را گسترش دهیم، بدون اینکه آن را بهطور دائمی تغییر دهیم. اما قبل از اینکه به طور عمیق وارد دکوراتورها شویم، بیایید مفاهیمی را که در یادگیری دکوراتورها مفید خواهند بود، درک کنیم. در پایتون، توابع به عنوان اشیا «Object functions» درجه یک «First Class Objects» شناخته میشوند، به این معنا که توابع در پایتون میتوانند به عنوان آرگومان «Argument» ارسال شوند یا به طور مستقیم استفاده شوند.
ویژگیهای توابع درجه یک
- یک تابع، نمونهای از نوع شیء «Object» است.
- میتوانید تابع را در یک متغیر «Variable» ذخیره کنید.
- میتوانید تابع را به عنوان پارامتر به یک تابع دیگر ارسال کنید.
- میتوانید تابع را از یک تابع دیگر بازگردانید.
- میتوانید آنها را در ساختارهای دادهای «Data structures» مانند جدولهای هش «Hash Table» ، لیستها «Lists» و غیره ذخیره کنید.
برای درک بهتر، مثالهای زیر را بررسی کنید:
# Python program to illustrate functions # can be treated as objects def shout(text): return text.upper() print(shout('Hello')) yell = shout print(yell('Hello'))
خروجی:
HELLO HELLO
در مثال بالا، تابع shout را به یک متغیر اختصاص دادهایم. این کار تابع را فراخوانی نمیکند، بلکه شی تابعی که توسط shout اشاره میشود را میگیرد و یک نام دوم به نام yell به آن اختصاص میدهد.
مثال ۲: ارسال تابع به عنوان آرگومان
# Python program to illustrate functions # can be passed as arguments to other functions def shout(text): return text.upper() def whisper(text): return text.lower() def greet(func): # storing the function in a variable greeting = func("""Hi, I am created by a function passed as an argument.""") print (greeting) greet(shout) greet(whisper)
خروجی:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT. hi, i am created by a function passed as an argument.
در مثال بالا، تابع greet یک تابع دیگر را به عنوان پارامتر میگیرد (در اینجا shout و whisper). سپس تابع ارسالشده به عنوان آرگومان درون تابع greet فراخوانی میشود.
مثال ۳: بازگرداندن توابع از یک تابع دیگر
# Python program to illustrate functions # Functions can return another function def create_adder(x): def adder(y): return x+y return adder add_15 = create_adder(15) print(add_15(10))
خروجی:
۲۵
در مثال بالا، ما یک تابع را درون تابع دیگری ایجاد کردهایم و سپس تابع داخلی را بازگرداندهایم.
دکوراتورها در پایتون
همچنین، دکوراتورها در پایتون برای تغییر رفتار یک تابع یا کلاس «Class» استفاده میشوند. در دکوراتورها، توابع به عنوان آرگومان به تابع دیگری ارسال میشوند و سپس درون تابع پوششی «wrapper function» فراخوانی میشوند.
@PS_decorator def hello_decorator(): print("PS") '''Above code is equivalent to - def hello_decorator(): print("PS") hello_decorator = PS_decorator(hello_decorator)'''
در کد بالا، PS_decorator یک تابع قابل فراخوانی است که کدی را به بالای یک تابع قابل فراخوانی دیگر، یعنی hello_decorator اضافه میکند و سپس تابع پوششی «Wrapper Function» را باز میگرداند.
دکوراتورها در پایتون میتوانند رفتار را تغییر دهند:
# defining a decorator def hello_decorator(func): # inner1 is a Wrapper function in # which the argument is called # inner function can access the outer local # functions like in this case "func" def inner1(): print("Hello, this is before function execution") # calling the actual function now # inside the wrapper function. func() print("This is after function execution") return inner1 # defining a function, to be called inside wrapper def function_to_be_used(): print("This is inside the function !!") # passing 'function_to_be_used' inside the # decorator to control its behaviour function_to_be_used = hello_decorator(function_to_be_used) # calling the function function_to_be_used()
خروجی:
Hello, this is before function execution This is inside the function !! This is after function execution
برای درک رفتار کد و نحوه اجرای آن مرحله به مرحله وقتی که تابع function_to_be_used فراخوانی میشود، مراحل زیر را دنبال کنید:
بیایید به مثالی دیگر بپردازیم که در آن میتوانیم بهسادگی زمان اجرای یک تابع را با استفاده از یک دکوراتور پیدا کنیم.
# importing libraries import time import math # decorator to calculate duration # taken by any function. def calculate_time(func): # added arguments inside the inner1, # if function takes any arguments, # can be added like this. def inner1(*args, **kwargs): # storing time before function execution begin = time.time() func(*args, **kwargs) # storing time after function execution end = time.time() print("Total time taken in : ", func.__name__, end - begin) return inner1 # this can be added to any function present, # in this case to calculate a factorial @calculate_time def factorial(num): # sleep 2 seconds because it takes very less time # so that you can see the actual difference time.sleep(2) print(math.factorial(num)) # calling the function. factorial(10)
خروجی:
۳۶۲۸۸۰۰ Total time taken in : factorial 2.007749557495117
ارسال و دریافت در دکوراتورها
در تمام مثالهای بالا، توابع چیزی برنمیگرداندند، بنابراین مشکلی وجود نداشت، اما ممکن است نیاز به مقدار بازگشتی داشته باشیم.
def hello_decorator(func): def inner1(*args, **kwargs): print("before Execution") # getting the returned value returned_value = func(*args, **kwargs) print("after Execution") # returning the value to the original frame return returned_value return inner1 # adding decorator to the function @hello_decorator def sum_two_numbers(a, b): print("Inside the function") return a + b a, b = 1, 2 # getting the value through return of the function print("Sum =", sum_two_numbers(a, b))
خروجی:
before Execution Inside the function after Execution Sum = 3
در مثال بالا، ممکن است متوجه تفاوت ظریفی در پارامترهای تابع داخلی شوید. تابع داخلی آرگومانها را به صورت args* و kwargs** دریافت میکند، به این معنی که یک تاپل «Tuple» از آرگومانهای موقعیتی یا یک دیکشنری «Dictionary» از آرگومانهای کلیدی با هر طولی میتواند ارسال شود. این ویژگی، دکورhتور را عمومی میکند و امکان تزئین توابع با هر تعداد آرگومان را فراهم میسازد.
زنجیرهسازی دکوراتورها در پایتون
به زبان ساده، زنجیرهسازی دکوراتورها به معنای تزئین یک تابع با چندین دکورhتور است.
# code for testing decorator chaining 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 @decor @decor1 def num2(): return 10 print(num()) print(num2())
خروجی:
۴۰۰ ۲۰۰
مثال بالا مشابه این است که تابع را به این صورت فراخوانی کنیم:
decor1(decor(num)) decor(decor1(num2))
نتیجه گیری
دکوراتورها در پایتون ابزار قدرتمندی برای بهبود خوانایی، قابلیت استفاده مجدد و انعطافپذیری کد هستند. با استفاده از آنها میتوان رفتار توابع را بدون تغییر مستقیم در کد اصلی تغییر داد. این ویژگی برای افزودن قابلیتهایی مانند مدیریت زمان اجرا، بررسی مجوزها، لاگگیری و موارد دیگر بسیار مفید است. علاوه بر این، دکوراتورها با قابلیت زنجیرهسازی و پشتیبانی از آرگومانهای مختلف، توسعهدهندگان را قادر میسازند تا به شیوهای ساده و کارآمد کدهای پیچیده بنویسند.