Python Programming

آشنایی با دکوراتورها و متا‌برنامه‌نویسی در پایتون: قدرت بخشیدن به کد شما

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

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

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

def my_decorator(func):
    def wrapper():
        print("قبل از اجرای تابع")
        func()
        print("بعد از اجرای تابع")
    return wrapper

@my_decorator
def say_hello():
    print("سلام، جهان!")

say_hello()

هنگام اجرای این کد، خروجی زیر را مشاهده می‌کنید:

قبل از اجرای تابع
سلام، جهان!
بعد از اجرای تابع

الگوهای پیشرفته دکوراتورها

دکوراتورها زمانی واقعاً قدرتمند می‌شوند که با استفاده از آرگومان‌ها و مدیریت درست اطلاعات تابع (metadata) پیاده‌سازی شوند. در اینجا یک مثال از دکوراتوری که آرگومان می‌گیرد آورده شده است:

import functools

def repeat(times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"سلام، {name}!")

greet("آلیس")

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

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

برای سناریوهای پیچیده‌تر، ممکن است بخواهید از دکوراتورهای مبتنی بر کلاس استفاده کنید. این نوع دکوراتورها زمانی بسیار مفید هستند که نیاز به حفظ وضعیت بین فراخوانی‌های تابع دارید:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
        functools.update_wrapper(self, func)
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"فراخوانی {self.count} به {self.func.__name__}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("سلام!")

say_hello()
say_hello()

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

متا‌برنامه‌نویسی با اینکه کد را به نحوی می‌نویسد که خود کلاس‌ها را دستکاری کند، انعطاف‌پذیری پایتون را به سطح بعدی می‌رساند. متاکلاس‌ها «کلاس‌های کلاس» هستند - آن‌ها نحوه رفتار کلاس‌ها را تعریف می‌کنند:

class SingletonMeta(type):
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta):
    def __init__(self):
        self.connection = "متصل به پایگاه داده"
    
    def query(self, sql):
        return f"در حال اجرا: {sql}"

# این اطمینان می‌دهد که فقط یک نمونه وجود دارد
db1 = Database()
db2 = Database()
print(db1 is db2)  # True

کاربردهای عملی در پروژه‌های واقعی

دکوراتورها و متا‌برنامه‌نویسی در برنامه‌های واقعی بسیار ارزشمند هستند. در اینجا یک مثال عملی از یک دکوراتور کش (cache) آورده شده است:

import time
from functools import wraps

def cache_result(func):
    cache = {}
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        key = str(args) + str(sorted(kwargs.items()))
        if key in cache:
            print("موفقیت در کش!")
            return cache[key]
        else:
            print("محاسبه نتیجه...")
            result = func(*args, **kwargs)
            cache[key] = result
            return result
    
    return wrapper

@cache_result
def expensive_calculation(n):
    time.sleep(1)  # شبیه‌سازی عملیات گران
    return n * n

# فراخوانی اول - محاسبه و ذخیره در کش
result1 = expensive_calculation(5)
# فراخوانی دوم - استفاده از کش
result2 = expensive_calculation(5)

روش‌های بهتر و اشتباهات رایج

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

نتیجه‌گیری

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

Share: