1 هفته قبل

بدون دیدگاه

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

20 دکوراتورهای کاربردی پایتون که باید بدانید!

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

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

در این مقاله، یاد خواهید گرفت:

  • دکوراتورهای پایتون چه هستند و چرا استفاده از آن‌ها مفید است.
  • چگونه می‌توان دکوراتورها را برای افزودن قابلیت‌های جدید به توابع و کلاس‌ها به کار گرفت.
  • معرفی و توضیح ۲۰ دکوراتور مهم و کاربردی، به همراه مثال برای هر یک.
  • چگونه از دکوراتورها برای مدیریت منابع، بهینه‌سازی عملکرد و کنترل دسترسی در کدنویسی استفاده کنید.

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

مقاله پیشین: دکوراتور در پایتون

staticmethod@

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

class MathOperations:
    @staticmethod
    def add(a, b):
        return a + b

# استفاده از متد استاتیک بدون نیاز به ایجاد نمونه
print(MathOperations.add(5, 10))  # خروجی: 15

classmethod@

این دکوراتور برای تعریف متدی به کار می‌رود که به خود کلاس به عنوان پارامتر cls دسترسی دارد، نه به یک نمونه خاص. از این دکوراتور برای دسترسی و کار با ویژگی‌ها یا متدهای کلاس (نه نمونه‌ها) استفاده می‌شود.

class Person:
    species = "Homo sapiens"

    @classmethod
    def print_species(cls):
        print(f"Species: {cls.species}")

# استفاده از متد کلاس بدون نیاز به ایجاد نمونه
Person.print_species()  # خروجی: Species: Homo sapiens

property@

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

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value > 0:
            self._radius = value
        else:
            raise ValueError("Radius must be positive")

circle = Circle(5)
print(circle.radius)  # خروجی: 5
circle.radius = 10
print(circle.radius)  # خروجی: 10

abstractmethod@

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

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Dog(Animal):
    def sound(self):
        return "Woof"

# استفاده از کلاس انتزاعی
dog = Dog()
print(dog.sound())  # خروجی: Woof

lru_cache@

این دکوراتور که در functools موجود است، نتایج توابع را برای بهبود عملکرد کش می‌کند و با ذخیره نتایج محاسبات قبلی باعث سرعت بخشیدن به عملیات می‌شود. این دکوراتور مخصوصاً برای توابعی که نیاز به محاسبات مکرر دارند مناسب است.

from functools import lru_cache

@lru_cache(maxsize=3)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # خروجی: 55

dataclass@

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

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

point = Point(5, 10)
print(point)  # خروجی: Point(x=5, y=10)

singledispatch@

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

from functools import singledispatch

@singledispatch
def process_data(data):
    raise NotImplementedError("Unsupported type")

@process_data.register(str)
def _(data):
    return data.upper()

@process_data.register(int)
def _(data):
    return data ** 2

print(process_data("hello"))  # خروجی: HELLO
print(process_data(5))  # خروجی: 25

wraps@

این دکوراتور که در functools موجود است، در دکوراتورهای سفارشی برای حفظ اطلاعات تابع اصلی به کار می‌رود، به طوری که نام و مستندات تابع اصلی پس از اعمال دکوراتور تغییر نکند و همچنان قابل دسترسی باشد.

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@decorator
def greet():
    print("Hello, World!")

greet()
# خروجی:
# Before function call
# Hello, World!
# After function call

contextmanager@

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

from contextlib import contextmanager

@contextmanager
def open_file(name):
    f = open(name, 'w')
    try:
        yield f
    finally:
        f.close()

with open_file('example.txt') as f:
    f.write('Hello, World!')

برای توضیحات بیشتر: آموزش Context Manager در پایتون

asyncio.coroutine@

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

توجه
استفاده از @asyncio.coroutine به عنوان دکوراتور در نسخه‌های جدیدتر پایتون منسوخ شده و به جای آن از async def استفاده می‌شود. نمونه کد زیر بر اساس روش جدید نوشته شده است.

import asyncio

async def say_hello():
    await asyncio.sleep(1)
    print("Hello, Async World!")

# اجرای تابع همزمانی
asyncio.run(say_hello())

total_ordering@

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

from functools import total_ordering

@total_ordering
class Number:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

a = Number(5)
b = Number(10)
print(a < b)  # خروجی: True

cache@

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

from functools import cache

@cache
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

print(factorial(5))  # خروجی: 120

همچنین توضیحات بیشتری رو می‌توانید در این مقاله پیدا کنید: کشینگ در پایتون با دکوراتر‌

final@

این دکوراتور از بازنویسی و ارث‌بری یک متد یا کلاس جلوگیری می‌کند. زمانی که می‌خواهید متد یا کلاسی کاملاً ثابت باشد و در زیرکلاس‌ها تغییر نکند، این دکوراتور بسیار کاربردی است.

from typing import final

class Animal:
    @final
    def sound(self):
        return "Some sound"

class Dog(Animal):
    pass  # این کلاس نمی‌تواند `sound` را بازنویسی کند

runtime_checkable@

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

from typing import Protocol, runtime_checkable

@runtime_checkable
class Runnable(Protocol):
    def run(self) -> None:
        ...

class MyRunnable:
    def run(self):
        print("Running!")

r = MyRunnable()
print(isinstance(r, Runnable))  # خروجی: True

beartype@

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

from beartype import beartype

@beartype
def add(a: int, b: int) -> int:
    return a + b

print(add(2, 3))  # خروجی: 5

overload@

این دکوراتور در typing امکان تعریف چندین امضای مختلف برای یک تابع واحد را فراهم می‌کند. این ویژگی امکان تعریف توابعی با چندین نوع پارامتر و بازدهی را فراهم می‌کند که موجب خوانایی بیشتر و چندشکلی در کد می‌شود.

from typing import overload

@overload
def process(value: int) -> int:
    ...

@overload
def process(value: str) -> str:
    ...

def process(value):
    if isinstance(value, int):
        return value * 2
    elif isinstance(value, str):
        return value.upper()

print(process(3))  # خروجی: 6
print(process("hello"))  # خروجی: HELLO

typechecked@

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

from typeguard import typechecked

@typechecked
def greet(name: str):
    print(f"Hello, {name}")

greet("Alice")  # خروجی: Hello, Alice

retry@

این دکوراتور که در tenacity وجود دارد، در صورت بروز خطا، تابع را بر اساس سیاست تعیین‌شده مجدد اجرا می‌کند. این دکوراتور برای سناریوهایی مثل اتصال به شبکه یا درخواست‌های ناپایدار مناسب است.

from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(3))
def flaky_function():
    print("Trying...")
    raise Exception("Failed")

try:
    flaky_function()
except:
    print("All retries failed.")

register@

این دکوراتور که در atexit استفاده می‌شود، توابعی را ثبت می‌کند که قرار است در زمان پایان برنامه اجرا شوند و برای بستن منابع یا انجام کارهای پایانی به کار می‌رود.

import atexit

def goodbye():
    print("Goodbye!")

atexit.register(goodbye)
print("Hello, World!")

measure_time@

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

import time
from functools import wraps

def measure_time(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

@measure_time
def slow_function():
    time.sleep(1)

slow_function()  # خروجی: slow_function took 1.0000 seconds

حرف آخر

دکوراتورها در پایتون ابزاری ضروری و انعطاف‌پذیر برای توسعه‌دهندگان هستند. هر دکوراتوری که در این مقاله معرفی شد، می‌تواند کدنویسی شما را ساده‌تر، کارآمدتر و امن‌تر کند. از دکوراتورهای پایه‌ای مانند @staticmethod و @property گرفته تا دکوراتورهای پیشرفته‌تری چون @singledispatch و @asyncio.coroutine، هر کدام با هدف خاصی طراحی شده‌اند و به‌کارگیری آن‌ها می‌تواند کد شما را حرفه‌ای‌تر کند. اگر از این دکوراتورها در پروژه‌های خود استفاده کرده‌اید یا تجربه‌های خاصی دارید، خوشحال می‌شویم که نظرات و تجربیات خود را در بخش دیدگاه‌ها با ما به اشتراک بگذارید. کدام دکوراتور برای شما مفیدتر بوده است؟

دیدگاهتان را بنویسید

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

پیشنهاد های کد اکسپلور