0Pricing
Python Academy · درس

functools: ‏lru_cache وcached_property

خزّن نتائج العمليات الحسابية المكلفة مؤقتاً باستخدام lru_cache وcached_property

functools: ‏lru_cache وcached_property درس مجاني في Python Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Python Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Python Academy 4 دروس في المجموع.

ما هو Memoization؟

يخزّن Memoization نتيجة استدعاء الدالة مؤقتًا، مع ربطها بوسائطها. وتُعيد الاستدعاءات المتكررة بالوسائط نفسها النتيجة المخزنة مؤقتًا فورًا.

def slow_fib(n):
    if n < 2: return n
    return slow_fib(n-1) + slow_fib(n-2)

# slow_fib(35) makes ~29 million calls
# With caching it makes only 35

@lru_cache

يخزّن @functools.lru_cache(maxsize=128) ما يصل إلى maxsize من النتائج الحديثة. اضبط maxsize=None لإنشاء ذاكرة تخزين مؤقتة غير محدودة.

import functools

@functools.lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

print(fib(50))  # instant

@cache — Python 3.9+

‏functools.cache اختصار لـ lru_cache(maxsize=None) — ذاكرة تخزين مؤقتة غير محدودة باسم أوضح.

import functools

@functools.cache
def factorial(n):
    return n * factorial(n-1) if n else 1

print(factorial(10))  # 3628800

معلومات الذاكرة المؤقتة ومسحها

توفّر الدوال ذات النتائج المخزنة مؤقتًا .cache_info() (عدد مرات الإصابة والإخفاق والحجم) و.cache_clear().

import functools

@functools.lru_cache(maxsize=100)
def square(n):
    return n * n

for i in range(5): square(i % 3)
print(square.cache_info())
# CacheInfo(hits=2, misses=3, maxsize=100, currsize=3)
square.cache_clear()

سياسة الإخلاء LRU

تُخلي LRU (الأقل استخدامًا مؤخرًا) العنصر الذي تم الوصول إليه بأقل تكرار عند امتلاء الذاكرة المؤقتة.

import functools

@functools.lru_cache(maxsize=3)
def compute(n):
    print(f"computing {n}")
    return n**2

for x in [1,2,3,4,1]:   # 4 evicts 1 (LRU), then 1 re-computes
    compute(x)

الوسائط القابلة للتجزئة فقط

تتطلب lru_cache أن تكون جميع الوسائط قابلة للتجزئة. القوائم والقواميس غير قابلة للتجزئة؛ لذا استخدم الصفوف بدلًا منها.

import functools

@functools.lru_cache(maxsize=None)
def sum_tuple(t):  # tuple is hashable
    return sum(t)

print(sum_tuple((1,2,3)))  # 6
# sum_tuple([1,2,3])  # TypeError

@cached_property

تحسب functools.cached_property خاصيةً مرة واحدة، ثم تخزّن النتيجة في الكائن، مستبدلةً الواصف بالقيمة.

import functools

class Circle:
    def __init__(self, r):
        self.r = r

    @functools.cached_property
    def area(self):
        import math
        print("computing...")
        return math.pi * self.r ** 2

c = Circle(5)
print(c.area)   # computing...  78.53...
print(c.area)   # 78.53... (cached, no print)

cached_property مقارنةً بـ property

تعيد @property الحساب عند كل وصول. أما @cached_property فتحسب مرة واحدة وتخزّن النتيجة في instance.__dict__.

import functools

class Expensive:
    @property
    def always(self):    # runs every access
        return sum(range(1_000_000))

    @functools.cached_property
    def once(self):      # runs only first access
        return sum(range(1_000_000))

أمان cached_property في الخيوط

لا تُعد cached_property آمنةً مع الخيوط. فإذا وصلت إليها خيوط متعددة في الوقت نفسه، فقد يُنفَّذ الحساب أكثر من مرة. استخدم قفلًا عند الحاجة.

import functools, threading

class SafeCache:
    _lock = threading.Lock()

    @functools.cached_property
    def data(self):
        with self._lock:
            return expensive_computation()

إبطال cached_property

احذف سمة الكائن لإبطال الذاكرة المؤقتة وفرض إعادة الحساب عند الوصول التالي.

import functools

class Report:
    @functools.cached_property
    def summary(self):
        return compute_summary()

r = Report()
_ = r.summary      # computed
del r.summary      # invalidate
_ = r.summary      # recomputed

استخدام lru_cache كذاكرة مؤقتة لواجهة API

خزّن استجابات API مؤقتًا خلال الجلسة لتجنّب استدعاءات الشبكة المكررة. امسح الذاكرة المؤقتة عند الحاجة إلى بيانات حديثة.

import functools, urllib.request, json

@functools.lru_cache(maxsize=32)
def get_user(user_id):
    url = f"https://api.example.com/users/{user_id}"
    with urllib.request.urlopen(url) as r:
        return json.loads(r.read())

user = get_user(42)   # network call
user = get_user(42)   # cached

تحقق سريع

ما الطريقة التي تمسح جميع النتائج المخزنة مؤقتًا لدالة مزينة باستخدام @lru_cache؟

مراجعة

تخزّن @lru_cache نتائج الدوال مؤقتًا مع ربطها بالوسائط، التي يجب أن تكون قابلة للتجزئة. و@cache اسم بديل غير محدود. أما @cached_property فتخزّن نتيجة حساب الخاصية مؤقتًا لكل كائن. افحصها باستخدام cache_info() وأعد ضبطها باستخدام cache_clear().

الأسئلة الشائعة

هل درس «functools: ‏lru_cache وcached_property» مجاني؟

نعم — نص درس «functools: ‏lru_cache وcached_property» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Python Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Python Academy 4 دروس في المجموع.

ماذا ستتعلم في «functools: ‏lru_cache وcached_property»؟

خزّن نتائج العمليات الحسابية المكلفة مؤقتاً باستخدام lru_cache وcached_property تتمرن على Python Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Python Academy؟

لا تُشترط خبرة سابقة. Python Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «functools: ‏lru_cache وcached_property»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Python Academy هذا؟

نعم. كل درس في Python Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. itertools: المكرّرات اللانهائية والمحدودة
  2. itertools: التوافقيات
  3. functools: ‏partial وreduce
  4. functools: ‏lru_cache وcached_property
← العودة إلى Python Academy