文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python如何使用LRU缓存策略进行缓存

2023-06-30 16:43

关注

本文小编为大家详细介绍“Python如何使用LRU缓存策略进行缓存”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python如何使用LRU缓存策略进行缓存”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

一、Python 缓存

① 缓存作用

② 使用 Python 字典实现缓存

以新闻聚合网站为例,不必每次都去下载文章内容,而是先检查缓存数据中是否存在对应的内容,只有当没有时,才会让服务器下载文章。

如下的示例程序,就是使用 Python 字典实现缓存的,将文章的 URL 作为键,并将其内容作为值;执行之后,可以看到当第二次执行 get_article 函数的时候,直接就返回结果并没有让服务器下载:

import requestscache = dict()def get_article_from_server(url):    print("Fetching article from server...")    response = requests.get(url)    return response.textdef get_article(url):    print("Getting article...")    if url not in cache:        cache[url] = get_article_from_server(url)    return cache[url]get_article("https://www.escapelife.site/love-python.html")get_article("https://www.escapelife.site/love-python.html")

将此代码保存到一个 caching.py 文件中,安装 requests 库,然后运行脚本:

# 安装依赖$ pip install requests# 执行脚本$ python python_caching.pyGetting article...Fetching article from server...Getting article...

尽管调用 get_article() 两次(第 17 行和第 18 行)字符串“Fetching article from server…”,但仍然只输出一次。发生这种情况的原因是,在第一次访问文章之后,将其 URL 和内容放入缓存字典中,第二次时代码不需要再次从服务器获取项目。

③ 使用字典来做缓存的弊端

上面这种缓存实现存在一个非常大的问题,那就是字典的内容将会无限增长,即大量用户连续浏览文章的时候,后台程序将不断向字典中塞入需要存储的内容,服务器内存被挤爆,最终导致应用程序崩溃。

缓存策略英文名称淘汰条件在什么时候最有用
先进先出算法(FIFO)First-In/First-Out淘汰最旧的条目较新的条目最有可能被重用
后进先出算法(LIFO)Last-In/First-Out淘汰最新的条目较旧的条目最有可能被重用
最近最少使用算法(LRU)Least Recently Used淘汰最近使用最少的条目最近使用的条目最有可能被重用
最近最多使用算法(MRU)Most Recently Used淘汰最近使用最多的条目最近不用的条目最有可能被重用
最近最少命中算法(LFU)Least Frequently Used淘汰最不经常访问的条目命中率很高的条目更有可能被重用

看了上述五种缓存算法,是不是看到 LRU 和 LFU 的时候有点懵,主要是通过中文对应的解释很难理解其真实的含义,看看英文的话就不难理解了。LRU 和 LFU 算法的不同之处在于:

比如,以十分钟为一个节点,每分钟进行一次页面调度,当所需的页面走向为 2 1 2 4 2 3 4 时,且调页面 4 时会发生缺页中断;若按 LRU 算法的话,应换页面 1(十分钟内页面 1 最久未被使用),但按 LFU 算法的话,应换页面 3(十分钟内页面 3 只使用一次)。

二、深入理解 LRU 算法

① 查看 LRU 缓存的特点

使用 LRU 策略实现的缓存是按照使用顺序进行排序的,每次访问条目时,LRU 算法就会将其移到缓存的顶部。通过这种方式,算法可以通过查看列表的底部,快速识别出最长时间未使用的条目。

如下所示,用户从网络上请求第一篇文章的 LRU 策略存储记录:

Python如何使用LRU缓存策略进行缓存

在将文章提供给用户之前,缓存如何将其存储在最近的槽中?如下所示,用户请求第二篇文章时发生的情况,第二篇文章存储到最上层的位置,即第二篇文章采用了最近的位置,将第一篇文章推到列表下方:

Python如何使用LRU缓存策略进行缓存

LRU 策略假定使用的对象越新,将来使用该对象的可能性就越大,因此它尝试将该对象保留在缓存中的时间最长,即如果发生条目淘汰的话,会优先淘汰第一篇文档的缓存存储记录。

② 查看 LRU 缓存的结构

在 Python 中实现 LRU 缓存的一种方法就是使用双向链表(doubly linked list)和哈希映射(hash map),双向链表的头元素将指向最近使用的条目,而其尾部将指向最近使用最少的条目。LRU 缓存实现逻辑结构如下:

Python如何使用LRU缓存策略进行缓存

通过使用哈希映射,可以将每个条目映射到双链表中的特定位置,从而确保对缓存中的每个项的访问。这个策略非常快,访问最近最少使用的项和更新缓存的复杂度均为 O(1) 操作。

而从 Python3.2 版本开始,Python 新增 @lru_cache 这个装饰器用于实现 LRU 策略,从此可以使用这个装饰器来装饰函数并缓存其计算结果。

三、使用 lru_cache 装饰器

① @lru_cache 装饰器的实现原理

有很多方法可以实现应用程序的快速响应,而使用缓存就是一种非常常见的方法。如果能够正确使用缓存的话,可以使响应变得更快且减少计算资源的额外负载。
在 Python 中 functools 模块自带了 @lru_cache 这个装饰器来做缓存,其能够使用最近最少使用(LRU)策略来缓存函数的计算结果,这是一种简单但功能强大的技术:

Python如何使用LRU缓存策略进行缓存

就像先前实现的缓存方案一样,Python 中的 @lru_cache 装饰器存储也是使用字典来做为存储对象的,它将函数的执行结果缓存在字典的 key 里面,该 key 由对该函数的调用(包括函数的参数)组成,这就意味着这些函数的参数必须是可哈希的,装饰器才能正常工作。

② 斐波拉契数列

我们都应该知道斐波拉契数列的计算方式,常见的解决方式就是使用递归的思路:

Python如何使用LRU缓存策略进行缓存

递归的计算简洁并且直观,但是由于存在大量重复计算,实际运行效率很低,并且会占用较多的内存。但是这里并不是需要关注的重点,只是来作为演示示例而已:

# 匿名函数fib = lambda n: 1 if n <= 1 else fib(n-1) + fib(n-2)# 将时间复杂度降低到线性fib = lambda n, a=1, b=1: a if n == 0 else fib(n-1, b, a+b)# 保证了匿名函数的匿名性fib = lambda n, fib: 1 if n <= 1 else fib(n-1, fib) + fib(n-2, fib)

③ 使用 @lru_cache 缓存输出结果

使用 @lru_cache 装饰器来缓存的话,可以将函数调用结果存储在内存中,以便再次请求时直接返回结果:

from functools import lru_cache@lru_cachedef fib(n):    if n==1 or n==2:        return 1    else:        return fib(n-1) + fib(n-2)

④ 限制 @lru_cache 装饰器大小

Python 的 @lru_cache 装饰器提供了一个 maxsize 属性,该属性定义了在缓存开始淘汰旧条目之前的最大条目数,默认情况下,maxsize 设置为 128。

如果将 maxsize 设置为 None 的话,则缓存将无限期增长,并且不会驱逐任何条目。

from functools import lru_cache@lru_cache(maxsize=16)def fib(n):    if n==1 or n==2:        return 1    else:        return fib(n-1) + fib(n-2)
# 查看缓存列表>>> print(steps_to.cache_info())CacheInfo(hits=52, misses=30, maxsize=16, currsize=16)

⑤ 使用 @lru_cache 实现 LRU 缓存

就像在前面实现的缓存解决方案一样,@lru_cache 在底层使用一个字典,它将函数的结果缓存在一个键下,该键包含对函数的调用,包括提供的参数。这意味着这些参数必须是可哈希的,才能让 decorator 工作。

示例:玩楼梯:

想象一下,你想通过一次跳上一个、两个或三个楼梯来确定到达楼梯中的一个特定楼梯的所有不同方式,到第四个楼梯有多少条路?所有不同的组合如下所示:

Python如何使用LRU缓存策略进行缓存

可以这样描述,为了到达当前的楼梯,你可以从下面的一个、两个或三个楼梯跳下去,将能够到达这些点的跳跃组合的数量相加,便能够获得到达当前位置的所有可能方法。

例如到达第四个楼梯的组合数量将等于你到达第三、第二和第一个楼梯的不同方式的总数。如下所示,有七种不同的方法可以到达第四层楼梯:

Python如何使用LRU缓存策略进行缓存

注意给定阶梯的解是如何建立在较小子问题的答案之上的,在这种情况下,为了确定到达第四个楼梯的不同路径,可以将到达第三个楼梯的四种路径、到达第二个楼梯的两种路径以及到达第一个楼梯的一种路径相加。 这种方法称为递归,下面是一个实现这个递归的函数:

def steps_to(stair):    if stair == 1:        # You can reach the first stair with only a single step        # from the floor.        return 1    elif stair == 2:        # You can reach the second stair by jumping from the        # floor with a single two-stair hop or by jumping a single        # stair a couple of times.        return 2    elif stair == 3:        # You can reach the third stair using four possible        # combinations:        # 1. Jumping all the way from the floor        # 2. Jumping two stairs, then one        # 3. Jumping one stair, then two        # 4. Jumping one stair three times        return 4    else:        # You can reach your current stair from three different places:        # 1. From three stairs down        # 2. From two stairs down        # 2. From one stair down        #        # If you add up the number of ways of getting to those        # those three positions, then you should have your solution.        return (            steps_to(stair - 3)            + steps_to(stair - 2)            + steps_to(stair - 1)        )print(steps_to(4))

将此代码保存到一个名为 stairs.py 的文件中,并使用以下命令运行它:

$ python stairs.py7

太棒了,这个代码适用于 4 个楼梯,但是数一下要走多少步才能到达楼梯上更高的地方呢?将第 33 行中的楼梯数更改为 30,并重新运行脚本:

$ python stairs.py53798080

可以看到结果超过 5300 万个组合,这可真的有点多。

时间代码:

当找到第 30 个楼梯的解决方案时,脚本花了相当多的时间来完成。要获得基线,可以度量代码运行的时间,要做到这一点,可以使用 Python 的 timeit module,在第 33 行之后添加以下代码:

setup_code = "from __main__ import steps_to"36stmt = "steps_to(30)"37times = repeat(setup=setup_code, stmt=stmt, repeat=3, number=10)38print(f"Minimum execution time: {min(times)}")

还需要在代码的顶部导入 timeit module:

from timeit import repeat

以下是对这些新增内容的逐行解释:

$ python stairs.py53798080Minimum execution time: 40.014977024000004

可以看到的秒数取决于特定硬件,在我的系统上,脚本花了 40 秒,这对于 30 级楼梯来说是相当慢的。

使用记忆来改进解决方案:

这种递归实现通过将其分解为相互构建的更小的步骤来解决这个问题,如下所示是一个树,其中每个节点表示对 steps_to() 的特定调用:

Python如何使用LRU缓存策略进行缓存

注意需要如何使用相同的参数多次调用 steps_to(),例如 steps_to(5) 计算两次,steps_to(4) 计算四次,steps_to(3) 计算七次,steps_to(2) 计算六次,多次调用同一个函数会增加不必要的计算周期,结果总是相同的。

为了解决这个问题,可以使用一种叫做记忆的技术,这种方法将函数的结果存储在内存中,然后在需要时引用它,从而确保函数不会为相同的输入运行多次,这个场景听起来像是使用 Python 的 @lru_cache 装饰器的绝佳机会。

只要做两个改变,就可以大大提高算法的运行时间:

下面是两个更新后的脚本顶部的样子:

from functools import lru_cachefrom timeit import repeat @lru_cachedef steps_to(stair):if stair == 1:

运行更新后的脚本产生如下结果:

$ python stairs.py53798080Minimum execution time: 7.999999999987184e-07

缓存函数的结果会将运行时从 40 秒降低到 0.0008 毫秒,这是一个了不起的进步。@lru_cache 装饰器存储了每个不同输入的 steps_to() 的结果,每次代码调用带有相同参数的函数时,它都直接从内存中返回正确的结果,而不是重新计算一遍答案,这解释了使用 @lru_cache 时性能的巨大提升。

⑥ 解包 @lru_cache 的功能

有了@lru_cache 装饰器,就可以将每个调用和应答存储在内存中,以便以后再次请求时进行访问,但是在内存耗尽之前,可以节省多少次调用呢?

Python 的 @lru_cache 装饰器提供了一个 maxsize 属性,它定义了在缓存开始清除旧条目之前的最大条目数,缺省情况下,maxsize 设置为 128,如果将 maxsize 设置为 None,那么缓存将无限增长,并且不会驱逐任何条目。如果在内存中存储大量不同的调用,这可能会成为一个问题。

如下是 @lru_cache 使用 maxsize 属性:

from functools import lru_cachefrom timeit import repeat@lru_cache(maxsize=16)def steps_to(stair):    if stair == 1:

在本例中,将缓存限制为最多 16 个条目,当一个新调用传入时,decorator 的实现将会从现有的 16 个条目中删除最近最少使用的条目,为新条目腾出位置。

要查看添加到代码中的新内容会发生什么,可以使用 @lru_cache 装饰器提供的 cache_info() 来检查命中和未命中的次数以及当前缓存的大小。为了清晰起见,删除乘以函数运行时的代码,以下是修改后的最终脚本:

from functools import lru_cachefrom timeit import repeat@lru_cache(maxsize=16)def steps_to(stair):    if stair == 1:        # You can reach the first stair with only a single step        # from the floor.        return 1    elif stair == 2:        # You can reach the second stair by jumping from the        # floor with a single two-stair hop or by jumping a single        # stair a couple of times.        return 2    elif stair == 3:        # You can reach the third stair using four possible        # combinations:        # 1. Jumping all the way from the floor        # 2. Jumping two stairs, then one        # 3. Jumping one stair, then two        # 4. Jumping one stair three times        return 4    else:        # You can reach your current stair from three different places:        # 1. From three stairs down        # 2. From two stairs down        # 2. From one stair down        #        # If you add up the number of ways of getting to those        # those three positions, then you should have your solution.        return (            steps_to(stair - 3)            + steps_to(stair - 2)            + steps_to(stair - 1)        )print(steps_to(30))print(steps_to.cache_info())

如果再次调用脚本,可以看到如下结果:

$ python stairs.py53798080CacheInfo(hits=52, misses=30, maxsize=16, currsize=16)

可以使用 cache_info() 返回的信息来了解缓存是如何执行的,并对其进行微调,以找到速度和存储之间的适当平衡。下面是 cache_info() 提供的属性的详细说明:

如果需要从缓存中删除所有条目,那么可以使用 @lru_cache 提供的 cache_clear()。

四、添加缓存过期

假设想要开发一个脚本来监视 Real Python 并在任何包含单词 Python 的文章中打印字符数。真正的 Python 提供了一个 Atom feed,因此可以使用 feedparser 库来解析提要,并使用请求库来加载本文的内容。

如下是监控脚本的实现:

import feedparserimport requestsimport sslimport timeif hasattr(ssl, "_create_unverified_context"):    ssl._create_default_https_context = ssl._create_unverified_contextdef get_article_from_server(url):    print("Fetching article from server...")    response = requests.get(url)    return response.textdef monitor(url):    maxlen = 45    while True:        print("\nChecking feed...")        feed = feedparser.parse(url)        for entry in feed.entries[:5]:            if "python" in entry.title.lower():                truncated_title = (                    entry.title[:maxlen] + "..."                    if len(entry.title) > maxlen                    else entry.title                )                print(                    "Match found:",                    truncated_title,                    len(get_article_from_server(entry.link)),                )        time.sleep(5)monitor("https://realpython.com/atom.xml")

将此脚本保存到一个名为 monitor.py 的文件中,安装 feedparser 和请求库,然后运行该脚本,它将持续运行,直到在终端窗口中按 Ctrl+C 停止它:

$ pip install feedparser requests$ python monitor.pyChecking feed...Fetching article from server...The Real Python Podcast – Episode #28: Using ... 29520Fetching article from server...Python Community Interview With David Amos 54256Fetching article from server...Working With Linked Lists in Python 37099Fetching article from server...Python Practice Problems: Get Ready for Your ... 164888Fetching article from server...The Real Python Podcast – Episode #27: Prepar... 30784Checking feed...Fetching article from server...The Real Python Podcast – Episode #28: Using ... 29520Fetching article from server...Python Community Interview With David Amos 54256Fetching article from server...Working With Linked Lists in Python 37099Fetching article from server...Python Practice Problems: Get Ready for Your ... 164888Fetching article from server...The Real Python Podcast – Episode #27: Prepar... 30784

代码解释:

每当脚本加载一篇文章时,“Fetching article from server&hellip;”的消息就会打印到控制台,如果让脚本运行足够长的时间,那么将看到这条消息是如何反复显示的,即使在加载相同的链接时也是如此。

这是一个很好的机会来缓存文章的内容,并避免每五秒钟访问一次网络,可以使用 @lru_cache 装饰器,但是如果文章的内容被更新,会发生什么呢?第一次访问文章时,装饰器将存储文章的内容,并在以后每次返回相同的数据;如果更新了帖子,那么监视器脚本将永远无法实现它,因为它将提取存储在缓存中的旧副本。要解决这个问题,可以将缓存条目设置为过期。

from functools import lru_cache, wrapsfrom datetime import datetime, timedeltadef timed_lru_cache(seconds: int, maxsize: int = 128):    def wrapper_cache(func):        func = lru_cache(maxsize=maxsize)(func)        func.lifetime = timedelta(seconds=seconds)        func.expiration = datetime.utcnow() + func.lifetime        @wraps(func)        def wrapped_func(*args, **kwargs):            if datetime.utcnow() >= func.expiration:                func.cache_clear()                func.expiration = datetime.utcnow() + func.lifetime            return func(*args, **kwargs)        return wrapped_func    return wrapper_cache@timed_lru_cache(10)def get_article_from_server(url):    ...

代码解释:

第 4 行:@timed_lru_cache 装饰器将支持缓存中条目的生命周期(以秒为单位)和缓存的最大大小;

第 6 行:代码用 lru_cache 装饰器包装了装饰函数,这允许使用 lru_cache 已经提供的缓存功能;

第 7 行和第 8 行:这两行用两个表示缓存生命周期和它将过期的实际日期的属性来修饰函数;

第 12 到 14 行:在访问缓存中的条目之前,装饰器检查当前日期是否超过了过期日期,如果是这种情况,那么它将清除缓存并重新计算生存期和过期日期。

请注意,当条目过期时,此装饰器如何清除与该函数关联的整个缓存,生存期适用于整个缓存,而不适用于单个项目,此策略的更复杂实现将根据条目的单个生存期将其逐出。

在程序中,如果想要实现不同缓存策略,可以查看 cachetools 这个库,该库提供了几个集合和修饰符,涵盖了一些最流行的缓存策略。

使用新装饰器缓存文章:

现在可以将新的 @timed_lru_cache 装饰器与监视器脚本一起使用,以防止每次访问时获取文章的内容。为了简单起见,把代码放在一个脚本中,可以得到以下结果:

import feedparserimport requestsimport sslimport timefrom functools import lru_cache, wrapsfrom datetime import datetime, timedeltaif hasattr(ssl, "_create_unverified_context"):    ssl._create_default_https_context = ssl._create_unverified_contextdef timed_lru_cache(seconds: int, maxsize: int = 128):    def wrapper_cache(func):        func = lru_cache(maxsize=maxsize)(func)        func.lifetime = timedelta(seconds=seconds)        func.expiration = datetime.utcnow() + func.lifetime        @wraps(func)        def wrapped_func(*args, **kwargs):            if datetime.utcnow() >= func.expiration:                func.cache_clear()                func.expiration = datetime.utcnow() + func.lifetime            return func(*args, **kwargs)        return wrapped_func    return wrapper_cache@timed_lru_cache(10)def get_article_from_server(url):    print("Fetching article from server...")    response = requests.get(url)    return response.textdef monitor(url):    maxlen = 45    while True:        print("\nChecking feed...")        feed = feedparser.parse(url)        for entry in feed.entries[:5]:            if "python" in entry.title.lower():                truncated_title = (                    entry.title[:maxlen] + "..."                    if len(entry.title) > maxlen                    else entry.title                )                print(                    "Match found:",                    truncated_title,                    len(get_article_from_server(entry.link)),                )        time.sleep(5)monitor("https://realpython.com/atom.xml")

请注意第 30 行如何使用 @timed_lru_cache 装饰 get_article_from_server() 并指定 10 秒的有效性。在获取文章后的 10 秒内,任何试图从服务器访问同一篇文章的尝试都将从缓存中返回内容,而不会到达网络。

运行脚本并查看结果:

$ python monitor.pyChecking feed...Fetching article from server...Match found: The Real Python Podcast – Episode #28: Using ... 29521Fetching article from server...Match found: Python Community Interview With David Amos 54254Fetching article from server...Match found: Working With Linked Lists in Python 37100Fetching article from server...Match found: Python Practice Problems: Get Ready for Your ... 164887Fetching article from server...Match found: The Real Python Podcast – Episode #27: Prepar... 30783Checking feed...Match found: The Real Python Podcast – Episode #28: Using ... 29521Match found: Python Community Interview With David Amos 54254Match found: Working With Linked Lists in Python 37100Match found: Python Practice Problems: Get Ready for Your ... 164887Match found: The Real Python Podcast – Episode #27: Prepar... 30783Checking feed...Match found: The Real Python Podcast – Episode #28: Using ... 29521Match found: Python Community Interview With David Amos 54254Match found: Working With Linked Lists in Python 37100Match found: Python Practice Problems: Get Ready for Your ... 164887Match found: The Real Python Podcast – Episode #27: Prepar... 30783Checking feed...Fetching article from server...Match found: The Real Python Podcast – Episode #28: Using ... 29521Fetching article from server...Match found: Python Community Interview With David Amos 54254Fetching article from server...Match found: Working With Linked Lists in Python 37099Fetching article from server...Match found: Python Practice Problems: Get Ready for Your ... 164888Fetching article from server...Match found: The Real Python Podcast – Episode #27: Prepar... 30783

请注意,代码在第一次访问匹配的文章时是如何打印“Fetching article from server&hellip;”这条消息的。之后,根据网络速度和计算能力,脚本将从缓存中检索文章一两次,然后再次访问服务器。

该脚本试图每 5 秒访问这些文章,缓存每 10 秒过期一次。对于实际的应用程序来说,这些时间可能太短,因此可以通过调整这些配置来获得显著的改进。

五、@lru_cache 装饰器的官方实现

简单理解,其实就是一个装饰器:

def lru_cache(maxsize=128, typed=False):    if isinstance(maxsize, int):        if maxsize < 0:            maxsize = 0    elif callable(maxsize) and isinstance(typed, bool):        user_function, maxsize = maxsize, 128        wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)        return update_wrapper(wrapper, user_function)    elif maxsize is not None:        raise TypeError('Expected first argument to be an integer, a callable, or None')    def decorating_function(user_function):        wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)        return update_wrapper(wrapper, user_function)    return decorating_function
_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):    sentinel = object()          # unique object used to signal cache misses    make_key = _make_key         # build a key from the function arguments    PREV, NEXT, KEY, RESULT = 0, 1, 2, 3   # names for the link fields    cache = {}  # 存储也使用的字典    hits = misses = 0    full = False    cache_get = cache.get    cache_len = cache.__len__    lock = RLock()                      # 因为双向链表的更新不是线程安全的所以需要加锁    root = []                           # 双向链表    root[:] = [root, root, None, None]  # 初始化双向链表    if maxsize == 0:        def wrapper(*args, **kwds):            # No caching -- just a statistics update            nonlocal misses            misses += 1            result = user_function(*args, **kwds)            return result    elif maxsize is None:        def wrapper(*args, **kwds):            # Simple caching without ordering or size limit            nonlocal hits, misses            key = make_key(args, kwds, typed)            result = cache_get(key, sentinel)            if result is not sentinel:                hits += 1                return result            misses += 1            result = user_function(*args, **kwds)            cache[key] = result            return result    else:        def wrapper(*args, **kwds):            # Size limited caching that tracks accesses by recency            nonlocal root, hits, misses, full            key = make_key(args, kwds, typed)            with lock:                link = cache_get(key)                if link is not None:                    # Move the link to the front of the circular queue                    link_prev, link_next, _key, result = link                    link_prev[NEXT] = link_next                    link_next[PREV] = link_prev                    last = root[PREV]                    last[NEXT] = root[PREV] = link                    link[PREV] = last                    link[NEXT] = root                    hits += 1                    return result                misses += 1            result = user_function(*args, **kwds)            with lock:                if key in cache:                    pass                elif full:                    oldroot = root                    oldroot[KEY] = key                    oldroot[RESULT] = result                    root = oldroot[NEXT]                    oldkey = root[KEY]                    oldresult = root[RESULT]                    root[KEY] = root[RESULT] = None                    del cache[oldkey]                    cache[key] = oldroot                else:                    last = root[PREV]                    link = [last, root, key, result]                    last[NEXT] = root[PREV] = cache[key] = link                    full = (cache_len() >= maxsize)            return result    def cache_info():        """Report cache statistics"""        with lock:            return _CacheInfo(hits, misses, maxsize, cache_len())    def cache_clear():        """Clear the cache and cache statistics"""        nonlocal hits, misses, full        with lock:            cache.clear()            root[:] = [root, root, None, None]            hits = misses = 0            full = False    wrapper.cache_info = cache_info    wrapper.cache_clear = cache_clear    return wrapper

读到这里,这篇“Python如何使用LRU缓存策略进行缓存”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯