کش کردن نتایج توابع با functools.cache و functools.lru_cache
گاهی اوقات توابعی داریم که انجام محاسبات سنگین یا تکراری باعث کندی برنامه میشود. در این موارد، اگر نتیجه تابع برای ورودیهای یکسان قبلاً محاسبه شده باشد، میتوانیم آن نتیجه را ذخیره کنیم و دفعه بعد مستقیماً از آن استفاده کنیم. به این روش کش کردن (caching) میگویند. در پایتون با استفاده از ماژول functools
، دو ابزار مهم برای کش کردن داریم: cache
و lru_cache
.
functools.cache چیست؟
تابع cache
از پایتون 3.9 به بعد اضافه شده و تمام نتایج محاسبه شده یک تابع را در حافظه ذخیره میکند. این یعنی اگر یک بار تابع با یک ورودی اجرا شده باشد، در اجرای بعدی برای همان ورودی، نتیجه بلافاصله بازگردانده میشود بدون اجرای دوبارهی تابع.
مثال ساده با functools.cache
from functools import cache
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # خروجی: 55
در این مثال:
- تابع
fibonacci
به صورت بازگشتی تعریف شده است. - با استفاده از
@cache
، نتایج هر فراخوانی برای مقدار n ذخیره میشود. - این کار باعث کاهش تعداد محاسبات و افزایش سرعت اجرای تابع میشود.
functools.lru_cache چیست؟
تابع lru_cache
نیز مشابه cache
عمل میکند، اما یک قابلیت مهم اضافه دارد: فقط تعداد مشخصی از نتایج آخر را در حافظه نگه میدارد. وقتی کش پر شود، قدیمیترین نتایج حذف میشوند (Least Recently Used یا کماستفادهترین). این برای کنترل حافظه بسیار مفید است.
مثال با functools.lru_cache
from functools import lru_cache
@lru_cache(maxsize=3)
def multiply(a, b):
print(f"Calculating {a} * {b}")
return a * b
print(multiply(2, 5)) # محاسبه و ذخیره نتیجه
print(multiply(3, 4)) # محاسبه و ذخیره نتیجه
print(multiply(2, 5)) # استفاده از کش (چاپ نمیشود)
print(multiply(5, 6)) # محاسبه و ذخیره نتیجه
print(multiply(7, 8)) # محاسبه و ذخیره نتیجه، نتیجه قدیمی حذف میشود
print(multiply(3, 4)) # دوباره محاسبه میشود چون قبلاً حذف شده
در این مثال:
maxsize=3
یعنی فقط 3 نتیجه آخر ذخیره میشود.- وقتی نتیجه بیش از این مقدار شود، قدیمیترین نتیجه حذف میگردد.
- در مثال، فراخوانی مجدد
multiply(3,4)
بعد از حذف شدن از کش باعث اجرای دوباره تابع میشود.
چه زمانی از cache و lru_cache استفاده کنیم؟
- توابع محاسباتی سنگین که برای ورودیهای تکراری نتایج مشابه دارند.
- وقتی حافظه کافی برای نگهداری نتایج داریم و سرعت اجرای برنامه اهمیت دارد.
- در
lru_cache
برای کنترل حافظه و جلوگیری از اشغال بیش از حد مناسب است.
نکات مهم
- توابع کش شده باید فقط به ورودیهایشان وابسته باشند (بدون حالتهای جانبی یا تغییر دادهها).
- ورودیهای تابع باید قابل هش شدن (hashable) باشند، مانند اعداد، رشتهها یا تاپلها.
- در استفاده از کش باید مراقب مصرف حافظه باشیم، مخصوصاً برای دادههای بزرگ.