Skip to Content

Welcome!

This community is for professionals and enthusiasts of our products and services.
Share and discuss the best content and new marketing ideas, build your professional profile and become a better marketer together.

شما نیاز به ثبت‌نام دارید تا بتوانید با جامعه تعامل داشته باشید.
این سوال علامت‌گذاری شده است
1 پاسخ
5 نماها

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

توی پایتون

از دکوریتور هم استفاده کنه

مثلا تابع زیر رو در نظر بگیر:


def function_cal_1(self, *args, **kwargs):
......

بتونه به صورت زیر باز نویسی کنم:


@cache(duration=256)
def function_cal_1(self, *args, **kwargs):
...


بعد اگه دوباره این تابع فراخوانی بشه خروجی قبلی استفاده بشه.

آواتار
رها کردن
مولف پاسخ برتر

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

  • بر اساس نام تابع + آرگومان‌ها کلید می‌سازد
  • مدت اعتبار (duration) دارد
  • برای method‌های داخل کلاس هم کار می‌کند (چون self را در کلید دخالت نمی‌دهیم)
  • قابل توسعه برای Redis، فایل و ... است

 نسخه آماده استفاده: دکوریتور cache با expiration

استفاده:

class MyClass:
    @cache(duration=256)
    def function_cal_1(self, x, y):
        print("Real calculation running...")
        return x * y

اگر دوباره همین ورودی‌ها را صدا بزنی:

obj = MyClass()
obj.function_cal_1(2, 5)
obj.function_cal_1(2, 5)

دفعه دوم خروجی از کش برمی‌گردد و دیگر "Real calculation running..." چاپ نمی‌شود.

پیاده‌سازی دکوریتور cache

این همان چیزی است که نیاز داری:

import time
import functools
import hashlib
import json

_cache_storage = {}  # memory cache: { key: (expire_at, value) }


def make_cache_key(func, args, kwargs):
    """تولید یک کلید یکتا بر اساس نام تابع + پارامترها"""

    # args[0] = self → نباید در کش دخیل شود
    pure_args = args[1:] if args and hasattr(args[0], "__class__") else args

    raw_key = {
        "func": f"{func.__module__}.{func.__qualname__}",
        "args": pure_args,
        "kwargs": kwargs,
    }

    raw_str = json.dumps(raw_key, sort_keys=True)
    return hashlib.sha256(raw_str.encode()).hexdigest()


def cache(duration=60):
    """دکوریتور اصلی کش"""

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = make_cache_key(func, args, kwargs)
            now = time.time()

            # اگر قبلاً کش شده و هنوز معتبر است
            if key in _cache_storage:
                expire_at, value = _cache_storage[key]
                if expire_at > now:
                    return value  # برگرداندن از کش

            # اجرای واقعی تابع
            value = func(*args, **kwargs)

            # ذخیره در کش
            _cache_storage[key] = (now + duration, value)
            return value

        return wrapper

    return decorator

 نکات مهم

✔ کار می‌کند برای:

  • توابع معمولی
  • متدهای کلاس‌ها
  • آرگومان‌های معمولی (int, str, dict, list,…)

✔ برای self کش جداگانه نمی‌سازد

یعنی اگر دو آبجکت different باشند ولی ورودی‌ها یکسان، کش مشترک استفاده می‌شود

(در ۹۹٪ موارد همین مطلوب است)

✔ پایان زمان (Expiration)

بعد از اتمام duration دوباره مقدار جدید محاسبه می‌شود.

⚡ اگر کش دائمی بخواهی (مثلاً روی دیسک یا redis)

خبر بده تا نسخه‌های زیر را برات بسازم:

  • کش روی فایل (pickle)
  • کش روی SQLite
  • کش روی Redis
  • کش async مناسب برای FastAPI / aiohttp
  • کش LRU ترکیب‌شده با expiration


 کتابخانه‌های معروف برای کش کردن خروجی تابع‌ها در پایتون

 چند تا کتابخانه/ابزار آماده و محبوب وجود دارد که دقیقاً همین کار (یا بسیار شبیهش) را انجام می‌دهند. در ادامه مهم‌ترین‌ها را با توضیح می‌دهم 👇

کتابخانه / ماژول توضیح / ویژگی
functools (داخل خود پایتون) اگر تابع خالص داری (همان ورودی = همان خروجی، بدون side-effect) — می‌توانی با @functools.lru_cache(...) یا (در پایتون ≥ 3.9) با @functools.cache خروجی‌ها را کش کنی. (Python documentation)
cachetools یک کتابخانه انعطاف‌پذیر‌تر برای کش است؛ استراتژی‌های مختلف (TTL, LRU, LFU و غیره) + دکوریتورهایی مثل @cached, توانایی حذف یا منقضی کردن کش پس از زمان معین و ... (GeeksforGeeks)
cachier اگر می‌خواهی کش «پایدار» (persistent) داشته باشی — یعنی بعد از بستن برنامه یا ری‌استارت هم کش باقی بماند — cachier گزینه‌ مناسبی است. با دکوریتور ساده کار می‌کند و متدهای کلاس و توابع معمولی را پوشش می‌دهد. (GitHub)

⚠️ نقاط قوت و محدودیت هر کدام

  • functools.lru_cache / cache: سریع، ساده، در حافظه (RAM)، مناسب برای محاسبات سریع که ورودی‌ها hashable هستند. اما هیچ TTL / expiration زمانی ذاتی ندارند (مگر cache_clear() دستی بزنی)، و کش همیشه در حافظه می‌ماند. (Python documentation)
  • cachetools: انعطاف زیاد — TTL، eviction policy های مختلف، محدود کردن اندازه کش، مناسب برای مواردی که می‌خواهی کنترل دقیق روی کش داشته باشی. (GeeksforGeeks)
  • cachier: مناسب وقتی نتایج محاسبه‌شده‌ات را می‌خوای بین اجرای برنامه‌ها نگه داری — مثلاً در اسکریپت‌ها یا اپ‌هایی که چند بار راه‌اندازی می‌شوند، یا زمانی که محاسبه سنگین است و می‌خوای کش «دائم» باشد. (GitHub)

🎯 نظر من — چه زمانی از کدام استفاده کنی

  • اگر تابع ساده است و ورودی‌ها hashable: functools.lru_cache(maxsize=None) یا @functools.cache (پایتون ≥ 3.9) سریع‌ترین و ساده‌ترین انتخاب است.
  • اگر می‌خوای کش با expiration زمان یا eviction policy داشته باشی → cachetools مناسب‌تر است.
  • اگر نتیجه تابعت باید بین اجرای برنامه‌ها (یا بین restart) حفظ شود → cachier گزینه خوبی است.

اگر بخواهی — می‌توانم ۳–۴ کتابخانه معروف به همراه نمونه کد کامل برای هرکدام آماده کنم (برای متدهای کلاس، توابع ساده، با TTL، با persistence و غیره). مایل هستی اون نمونه‌ها را هم ببینی؟




آواتار
رها کردن