دکوراتورها در پایتون: آموزش انواع مختلف Decorator

بفرست برای دوستت
Telegram
WhatsApp
دکوراتورها در پایتون

فهرست مطالب

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

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

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

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

دکوراتورها در پایتون

دکوراتور (Decorator) در پایتون چیست؟

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

ساختار پایه یک دکوراتور

یک دکوراتور معمولاً شامل یک تابع درون‌تابعی (inner function) است که رفتار اصلی تابع ورودی را گسترش می‌دهد. نمونه‌ی ساده‌ی آن به شکل زیر است:

def simple_decorator(func):

    def wrapper():

        print("قبل از اجرای تابع")

        func()

        print("بعد از اجرای تابع")

    return wrapper

# استفاده از دکوراتور

@simple_decorator

def say_hello():

    print("سلام دنیا!")


say_hello()

خروجی برنامه:

قبل از اجرای تابع

سلام دنیا!

بعد از اجرای تابع

در این مثال:

  • simple_decorator دکوراتوری است که رفتار اضافه شده قبل و بعد از اجرای تابع را تعریف می‌کند.
  • علامت @simple_decorator بالای تابع say_hello باعث می‌شود این تابع توسط دکوراتور پردازش شود.
  • تابع اصلی say_hello بدون تغییر، ویژگی جدیدی دریافت کرده است.

نکات کلیدی

  1. دکوراتورها می‌توانند چندین تابع را با همان رفتار مشترک به راحتی تغییر دهند، بدون اینکه کد هر تابع را ویرایش کنید.
  2. استفاده از دکوراتورها باعث خواناتر شدن کد و کاهش تکرار می‌شود.
  3. دکوراتورها بخش مهمی از متا‌برنامه‌نویسی در پایتون هستند و برای توسعه‌دهندگان حرفه‌ای بسیار کاربردی‌اند.

 

برای دریافت راهنمایی تخصصی و انتخاب دوره‌های مناسب با نیاز شما، با کارشناسان آموزشی ما از طریق شماره ۰۹۹۰۵۵۰۱۹۹۸ در ارتباط باشید

آموزش پایتون

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

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

1. زمان‌سنجی توابع

با استفاده از دکوراتورها می‌توان مدت زمان اجرای یک تابع را اندازه‌گیری کرد. این کاربرد به ویژه برای بهینه‌سازی عملکرد برنامه‌ها مفید است.

import time


def timer(func):

    def wrapper(*args, **kwargs):

        start_time = time.time()

        result = func(*args, **kwargs)

        end_time = time.time()

        print(f"زمان اجرای تابع {func.__name__}: {end_time - start_time} ثانیه")

        return result

    return wrapper


@timer

def long_running_function():

    time.sleep(2)


long_running_function()

2. اعتبارسنجی ورودی‌ها

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

def validate_input(func):

    def wrapper(x):

        if x < 0:

            raise ValueError("عدد باید مثبت باشد!")

        return func(x)

    return wrapper

@validate_input

def square_root(x):

    return x ** 0.5

print(square_root(9))

3. کش کردن نتایج

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

def cache(func):

    memo = {}

    def wrapper(x):

        if x not in memo:

            memo[x] = func(x)

        return memo[x]

    return wrapper

@cache

def fibonacci(n):

    if n <= 1:

        return n

    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

نکات کلیدی

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

به نقل از سایت geeksforgeeks:

« یک دکوراتور اساساً تابعی است که تابع دیگری را به عنوان آرگومان می‌گیرد و تابع جدیدی با عملکرد بهبود یافته برمی‌گرداند.»

دکوراتورها در پایتون

پیش‌نیازهای لازم برای یادگیری دکوراتورها

قبل از اینکه به سراغ دکوراتورهای پیچیده برویم، لازم است چند مفهوم پایه در پایتون را درک کنید:

  1. توابع به عنوان اشیاء (Functions as First-Class Objects)
    در پایتون، توابع مانند هر نوع داده دیگری قابل استفاده هستند. می‌توان آن‌ها را به عنوان آرگومان به تابع دیگر ارسال کرد، در متغیر ذخیره کرد و حتی بازگرداند.
def greet():

    print("سلام!")

say_hello = greet  # تابع در متغیر ذخیره می‌شود

say_hello()

توابع تو در تو (Nested Functions)

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

def outer():

    def inner():

        print("تابع داخلی")

    inner()

outer()

استفاده از *args و **kwargs

برای اینکه دکوراتورها بتوانند هر تابعی را با تعداد متغیر آرگومان‌ها مدیریت کنند، باید از *args و **kwargs استفاده کنید. def my_decorator(func):

    def wrapper(*args, **kwargs):

        return func(*args, **kwargs)

    return wrapper

ایجاد یک Decorator ساده

حالا که پیش‌نیازها را می‌دانیم، می‌توانیم یک دکوراتور ساده بسازیم که قبل و بعد از اجرای یک تابع پیام چاپ کند:

def simple_decorator(func):

    def wrapper(*args, **kwargs):

        print("شروع اجرای تابع")

        result = func(*args, **kwargs)

        print("پایان اجرای تابع")

        return result

    return wrapper

@simple_decorator

def say_hello(name):

    print(f"سلام {name}!")


say_hello("علی")

خروجی:

شروع اجرای تابع

سلام علی!

پایان اجرای تابع

نکات کلیدی

  • دکوراتور بدون تغییر کد اصلی تابع، رفتار جدید اضافه می‌کند.
  • استفاده از *args و **kwargs باعث می‌شود دکوراتور همه توابع با تعداد آرگومان مختلف را پشتیبانی کند.
  • این ساختار پایه برای ایجاد دکوراتورهای پیشرفته‌تر مانند دکوراتورهای با آرگومان و تو در تو ضروری است.

استفاده از Decoratorها با آرگومان‌ها

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

مثال: دکوراتور با آرگومان

فرض کنید می‌خواهیم تابعی را قبل از اجرا چند بار تکرار کنیم:

def repeat(n):

    def decorator(func):

        def wrapper(*args, **kwargs):

            for _ in range(n):

                func(*args, **kwargs)

        return wrapper

    return decorator

@repeat(3)

def say_hello(name):

    print(f"سلام {name}!")

say_hello("علی")

خروجی:

سلام علی!

سلام علی!

سلام علی!

در این مثال:

  1. repeat(n) تابعی است که آرگومان n را می‌گیرد.
  2. decorator(func) دکوراتور واقعی است که رفتار تکرار را تعریف می‌کند.
  3. wrapper(*args, **kwargs) اجرای تابع اصلی را مدیریت می‌کند.

نکات کلیدی

  • دکوراتورهای با آرگومان به شما امکان می‌دهند رفتار دینامیک ایجاد کنید.
  • این ساختار برای لاگ‌گیری سفارشی، زمان‌سنجی با دقت خاص و کنترل دسترسی بسیار کاربردی است.
  • معمولاً از سه سطح تو در تو استفاده می‌شود: تابع آرگومان‌گیر → دکوراتور اصلی →

دکوراتورهای تو در تو (Nested Decorators)

گاهی نیاز داریم چند رفتار مختلف را به یک تابع اضافه کنیم. برای این منظور می‌توانیم چند دکوراتور را به صورت زنجیره‌ای روی یک تابع اعمال کنیم. این کار باعث می‌شود کد مرتب، خوانا و منعطف باقی بماند.

مثال: چند دکوراتور روی یک تابع

def uppercase(func):

    def wrapper(*args, **kwargs):

        result = func(*args, **kwargs)

        return result.upper()

    return wrapper

def greet_decorator(func):

    def wrapper(*args, **kwargs):

        return f"سلام! {func(*args, **kwargs)}"

    return wrapper

@uppercase

@greet_decorator

def say_name(name):

    return name

print(say_name("علی"))

خروجی:

سلام! علی

و سپس به صورت UPPERCASE:

سلام! علی

در این مثال:

  • دکوراتور greet_decorator ابتدا متن را با سلام ترکیب می‌کند.
  • سپس دکوراتور uppercase متن نهایی را به حروف بزرگ تبدیل می‌کند.
  • ترتیب اعمال دکوراتورها از پایین به بالا است (@greet_decorator ابتدا اجرا می‌شود).

نکات کلیدی

  1. می‌توان چندین دکوراتور را روی یک تابع اعمال کرد و رفتارهای ترکیبی ایجاد کرد.
  2. ترتیب دکوراتورها مهم است؛ دکوراتور بالایی آخرین تغییر را اعمال می‌کند.
  3. این روش برای مدیریت دسترسی، لاگ‌گیری و زمان‌سنجی همزمان بسیار مفید است.

مثال‌های عملی دکوراتورها

مثال‌های عملی دکوراتورها

در این بخش، چند دکوراتور پرکاربرد و عملی را بررسی می‌کنیم و نکات هر کدام را در جدول ارائه می‌کنیم:

کاربرد

دکوراتور نکات مهم
زمان‌سنجی توابع @timer

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

اعتبارسنجی ورودی‌ها

@validate_input ورودی‌های تابع را قبل از اجرا بررسی می‌کند و از خطاهای احتمالی جلوگیری می‌کند.
کش کردن نتایج @cache

نتایج تابع را ذخیره می‌کند تا از محاسبات تکراری جلوگیری شود.

تبدیل متن به حروف بزرگ

@uppercase خروجی تابع را به حروف بزرگ تبدیل می‌کند. می‌تواند با سایر دکوراتورها ترکیب شود.
افزودن مقدمه به پیام‌ها @greet_decorator

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

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

import time

 
# زمان‌سنجی

def timer(func):

    def wrapper(*args, **kwargs):

        start_time = time.time()

        result = func(*args, **kwargs)

        end_time = time.time()

        print(f"زمان اجرای {func.__name__}: {end_time - start_time:.2f} ثانیه")

        return result

    return wrapper

 # کش کردن نتایج

def cache(func):

    memo = {}

    def wrapper(n):

        if n not in memo:

            memo[n] = func(n)

        return memo[n]

    return wrapper

 
@timer

@cache

def fibonacci(n):

    if n <= 1:

        return n

    return fibonacci(n-1) + fibonacci(n-2)


print(fibonacci(10))

خروجی:

زمان اجرای fibonacci: 0.00 ثانیه

55

اشتباهات رایج در استفاده از دکوراتورها

اشتباهات رایج در استفاده از دکوراتورها

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

1. فراموش کردن *args و **kwargs در wrapper

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

راهکار: همیشه wrapper دکوراتور را با *args و **kwargs تعریف کنید:

def decorator(func):

    def wrapper(*args, **kwargs):

        return func(*args, **kwargs)

    return wrapper

2. عدم استفاده از functools.wraps

اگر از functools.wraps استفاده نکنید، نام و مستندات تابع اصلی در wrapper از بین می‌رود و اشکال‌زدایی سخت می‌شود.

from functools import wraps


def decorator(func):

    @wraps(func)

    def wrapper(*args, **kwargs):

        return func(*args, **kwargs)

    return wrapper

3.اعمال نادرست چند دکوراتور

ترتیب دکوراتورها مهم است. اعمال نادرست باعث می‌شود دکوراتورها به ترتیب دلخواه اجرا نشوند.

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

 

4.پیچیدگی بیش از حد در decorator

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

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

 

نکات کلیدی

  • دکوراتورها ابزار قدرتمندی هستند اما نیاز به رعایت استانداردهای کدنویسی دارند.
  • استفاده از functools.wraps باعث حفظ مستندات و نام تابع اصلی می‌شود.
  • مرتب نگه داشتن کد و رعایت ترتیب دکوراتورها از خطاهای رایج جلوگیری می‌کند.
  • برای کسانی که تازه می‌خواهند وارد حوزه هوش مصنوعی شوند، یادگیری مفاهیم پایه‌ای پایتون و دکوراتورها، راهگشای مسیر آموزش هوش مصنوعی از صفر است.

دکوراتورها در پایتون

جمع‌بندی

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

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

  • دکوراتور ساده: برای افزودن رفتار اولیه به توابع.
  • دکوراتور با آرگومان: برای ایجاد رفتار پویا و قابل تنظیم.
  • دکوراتورهای تو در تو (Nested): برای ترکیب چند رفتار مختلف روی یک تابع.

همچنین یاد گرفتیم چگونه از دکوراتورها برای زمان‌سنجی، اعتبارسنجی ورودی‌ها، کش کردن نتایج و مدیریت خروجی‌ها استفاده کنیم و اشتباهات رایج مانند فراموش کردن *args و **kwargs یا عدم استفاده از functools.wraps را شناسایی و جلوگیری کنیم.

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

 

برای دریافت راهنمایی تخصصی و انتخاب دوره‌های مناسب با نیاز شما، با کارشناسان آموزشی ما از طریق شماره ۰۹۹۰۵۵۰۱۹۹۸ در ارتباط باشید

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

1-چگونه می‌توان چند دکوراتور را به صورت زنجیره‌ای روی یک تابع اعمال کرد؟

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

2-چه زمانی باید از دکوراتورهای سفارشی به جای توابع معمولی استفاده کنیم؟

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

3-آیا دکوراتورها فقط برای توابع کاربرد دارند؟

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

4-چرا استفاده از functools.wraps در دکوراتورها مهم است؟

استفاده از functools.wraps باعث می‌شود نام و مستندات تابع اصلی حفظ شود. بدون این ابزار، هنگام استفاده از دکوراتور، اطلاعات تابع اصلی از بین می‌رود و فرآیند اشکال‌زدایی و مستندسازی دشوار می‌شود.

لیست دروس دوره

مسیر یادگیری هوش مصنوعی

اگر سوالی در مورد این درس دارید، در کادر زیر بنویسید.

0 نظرات
قدیمی‌ترین
تازه‌ترین بیشترین رأی
بازخورد (Feedback) های اینلاین
مشاهده همه دیدگاه ها