دکوراتورها در پایتون — به زبان ساده

عکس شاخص دکوراتورها در پایتون

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

مقدمه

دکوراتورها در پایتون ابزار بسیار قدرتمند و مفیدی در پایتون هستند زیرا به برنامه‌نویسان این امکان را می‌دهند که رفتار یک تابع یا کلاس را تغییر دهند. دکوراتورها به ما اجازه می‌دهند که یک تابع دیگر را در بر بگیریم تا رفتار تابع در بر گرفته شده را گسترش دهیم، بدون این‌که آن را به‌طور دائمی تغییر دهیم. اما قبل از اینکه به طور عمیق وارد دکوراتورها شویم، بیایید مفاهیمی را که در یادگیری دکوراتورها مفید خواهند بود، درک کنیم. در پایتون، توابع به عنوان اشیا «Object functions» درجه یک «First Class Objects» شناخته می‌شوند، به این معنا که توابع در پایتون می‌توانند به عنوان آرگومان «Argument» ارسال شوند یا به طور مستقیم استفاده شوند.

ویژگی‌های توابع درجه یک

  1. یک تابع، نمونه‌ای از نوع شیء «Object» است.
  2. می‌توانید تابع را در یک متغیر «Variable» ذخیره کنید.
  3. می‌توانید تابع را به عنوان پارامتر به یک تابع دیگر ارسال کنید.
  4. می‌توانید تابع را از یک تابع دیگر بازگردانید.
  5. می‌توانید آن‌ها را در ساختارهای داده‌ای «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))

نتیجه گیری

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


سوالات متداول


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

دکوراتورها برای تغییر رفتار توابع یا متدها استفاده می‌شوند. زمانی از آن‌ها استفاده کنید که بخواهید قابلیت‌هایی مانند ثبت وقایع (Logging)، کش کردن (Caching) یا احراز هویت (Authentication) را به توابع موجود اضافه کنید، بدون اینکه کد اصلی آن‌ها را تغییر دهید. دکوراتورها به جداسازی وظایف و بهبود خوانایی کد کمک می‌کنند.

تفاوت بین تابع و دکوراتور در پایتون چیست؟

تابع( یک تابع در پایتون بلوکی از کد است که یک وظیفه خاص را انجام می‌دهد. توابع می‌توانند ورودی (آرگومان‌ها) دریافت کنند، آن‌ها را پردازش کرده و در صورت نیاز خروجی ارائه دهند.) دکوراتور (یک دکوراتور یک تابع مرتبه بالاتر است که یک تابع دیگر را به‌عنوان آرگومان می‌پذیرد، عملکردی به آن اضافه می‌کند و یک تابع جدید برمی‌گرداند. دکوراتورها امکان تغییر یا گسترش رفتار توابع یا متدها را فراهم می‌کنند.)

__init__ در پایتون چیست؟

__init__ یک متد خاص (سازنده) در کلاس‌های پایتون است. این متد به‌طور خودکار زمانی که یک نمونه جدید از کلاس ایجاد می‌شود، فراخوانی می‌شود. وظیفه آن مقداردهی به ویژگی‌های شیء یا انجام تنظیمات موردنیاز برای شیء است.

تفاوت بین دکوراتور و Annotation در پایتون چیست؟

دکوراتور(دکوراتور یک تابع است که رفتار یک تابع یا متد دیگر را تغییر می‌دهد. ) Annotation (حاشیه‌نویسی)( Annotation روشی برای افزودن متادیتا به آرگومان‌های تابع و مقادیر بازگشتی با استفاده از سینتکس `:` و `->` است. Annotation رفتار تابع را تغییر نمی‌دهد.)

میزان رضایتمندی
لطفاً میزان رضایت خودتان را از این مطلب با دادن امتیاز اعلام کنید.
[ امتیاز میانگین 0 از 0 نفر ]
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع و مراجع:
geeksforgeeks مجله پی استور programiz

دیدگاه‌ خود را بنویسید

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



برچسب‌ها:
پایتون


پیمایش به بالا