0Pricing
Python Academy · 课时

functools:lru_cache 与 cached_property

使用 lru_cache 和 cached_property 缓存开销较大的计算。

functools:lru_cache 与 cached_property 是 CoddyKit 上的免费 Python Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。

什么是记忆化?

记忆化会根据函数参数缓存函数调用的结果。使用相同参数重复调用时,会立即返回缓存的结果。

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」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。

「functools:lru_cache 与 cached_property」这节课中我会学到什么?

使用 lru_cache 和 cached_property 缓存开销较大的计算。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Python Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「functools:lru_cache 与 cached_property」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Python Academy 课中编写并运行代码吗?

能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. itertools:无限与有限迭代器
  2. itertools:组合数学
  3. functools:partial 与 reduce
  4. functools:lru_cache 与 cached_property
← 返回 Python Academy