Python缓存:两个简单的方法

缓存是一种用于提高应用程序性能的技术,它通过临时存储程序获得的结果,以便在以后需要时重用它们。

在本文中,我们将学习Python中的不同缓存技术,包括functools模块中的@ lru_cache和@ cache装饰器。

简单示例:Python缓存实现

要在Python中创建缓存,我们可以使用functools模块中的@cache装饰器。在下面的代码中,注意print()函数只执行一次:

import functools@functools.cache
def square(n):print(f"Calculating square of {n}")return n * n# Testing the cached function
print(square(4))  # Output: Calculating square of 4 \n 16
print(square(4))  # Output: 16 (cached result, no recalculation)

输出

Calculating square of 4
16
16

Python中的缓存是什么?

假设我们需要解决一个数学问题,花一个小时得到正确的答案。如果第二天我们必须解决同样的问题,那么重用我们以前的工作而不是从头开始会很有帮助。

Python中的缓存遵循类似的原则–它在函数调用中计算值时存储这些值,以便在再次需要时重用它们。这种类型的缓存也称为记忆化。

让我们看一个简短的例子,它计算了两次大范围的数字之和:

output = sum(range(100_000_001))
print(output)
output = sum(range(100_000_001))
print(output)

输出

5000000050000000
5000000050000000

计算两次运行时间:

import timeitprint(timeit.timeit("sum(range(100_000_001))",globals=globals(),number=1,)
)print(timeit.timeit("sum(range(100_000_001))",globals=globals(),number=1,)
)

输出

1.2157779589979327
1.1848394999979064

输出显示,两个调用所花费的时间大致相同(取决于我们的设置,我们可能会获得更快或更慢的执行时间)。

但是,我们可以使用缓存来避免多次计算相同的值。我们可以使用内置functools模块重新定义:

import functools
import timeitsum = functools.cache(sum)print(timeit.timeit("sum(range(100_000_001))",globals=globals(),number=1,)
)print(timeit.timeit("sum(range(100_000_001))",globals=globals(),number=1,)
)

输出

1.2760689580027247
2.3330067051574588e-06

第二个调用现在需要几微秒的时间,而不是一秒钟,因为从0到100,000,000的数字之和的结果已经计算并缓存了-第二个调用使用之前计算和存储的值。

functools.cache()装饰器是在Python 3.9版本中添加的,但我们可以在旧版本中使用functools.lru_cache()

Python缓存:不同的方法

Python的functools模块有两个装饰器用于将缓存应用于函数。让我们通过一个示例来探索functools.lru_cache()和functools.cache()。

让我们编写一个函数sum_digits(),它接受一个数字序列并返回这些数字的位数之和。例如,如果我们使用元组(23,43,8)作为输入,那么:

  • 23的数字之和是5
  • 43的数字之和是7
  • 8的数字之和是8
  • 因此,总和为20。

这是我们可以编写sum_digits函数的一种方式:

def sum_digits(numbers):return sum(int(digit) for number in numbers for digit in str(number))numbers = 23, 43, 8print(sum_digits(numbers))

输出

20

让我们使用这个函数来探索创建缓存的不同方法。

Python手动缓存

让我们首先手动创建该高速缓存。虽然我们也可以很容易地自动化,但手动创建缓存有助于我们理解这个过程。

让我们创建一个字典,并在每次使用新值调用函数时添加键值对来存储结果。如果我们用一个已经存储在这个字典中的值调用函数,函数将返回存储的值,而不会再次计算:

import random
import timeitdef sum_digits(numbers):if numbers not in sum_digits.my_cache:sum_digits.my_cache[numbers] = sum(int(digit) for number in numbers for digit in str(number))return sum_digits.my_cache[numbers]
sum_digits.my_cache = {}numbers = tuple(random.randint(1, 1000) for _ in range(1_000_000))print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)

输出

0.28875587500078836
0.0044607500021811575

第二次调用sum_digits(numbers)比第一次调用快得多,因为它使用了缓存的值。

现在让我们更详细地解释上面的代码。首先,请注意,我们在定义函数后创建了字典sum_digits.my_cache,即使我们在函数定义中使用了它。

函数的作用是:检查传递给函数的参数是否已经是sum_digits.my_cache字典中的键之一。仅当参数不在该高速缓存中时,才计算所有数字的和。

由于我们在调用函数时使用的参数作为字典中的键,因此它必须是可散列数据类型。列表是不可散列的,所以我们不能将它用作字典中的键。例如,让我们尝试用列表而不是元组来替换数字-这将引发TypeError:

# ...numbers = [random.randint(1, 1000) for _ in range(1_000_000)]# ...

输出

Traceback (most recent call last):
...
TypeError: unhashable type: 'list'

手动创建缓存非常适合学习,但现在让我们探索更快的方法。

使用functools.lru_cache()进行Python缓存

Python从3.2版开始就有了lru_cache()装饰器。函数名开头的“lru”代表“least recently used”。我们可以把缓存看作是一个用来存储经常使用的东西的盒子–当它填满时,LRU策略会扔掉我们很长时间没有使用过的东西,为新的东西腾出空间。

让我们用@functools.lru_cache来装饰sum_digits函数:

import functools
import random
import timeit@functools.lru_cache
def sum_digits(numbers):return sum(int(digit) for number in numbers for digit in str(number))numbers = tuple(random.randint(1, 1000) for _ in range(1_000_000))print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)

输出

0.28326129099878017
0.002184917000704445

多亏了缓存,第二个调用的运行时间大大缩短。

默认情况下,该高速缓存存储计算的前128个值。一旦所有128个位置都满了,算法就会删除最近最少使用的(LRU)值,为新值腾出空间。

当我们使用maxsize参数修饰函数时,我们可以设置不同的最大缓存大小:

import functools
import random
import timeit@functools.lru_cache(maxsize=5)
def sum_digits(numbers):return sum(int(digit) for number in numbers for digit in str(number))# ...

在这种情况下,该高速缓存仅存储5个值。如果我们不想限制该高速缓存的大小,也可以将maxsize参数设置为None。

使用functools.cache()进行Python缓存

Python 3.9包含了一个更简单、更快速的缓存装饰器——functools. cache()。这个装饰器有两个主要特点:

  • 它没有最大大小-这类似于调用functools.lru_cache(maxsize=None)。
  • 它存储所有函数调用及其结果(它不使用LRU策略)。这适用于输出相对较小的函数,或者当我们不需要担心缓存大小限制时。

让我们在sum_digits函数上使用@functools.cache装饰器:

import functools
import random
import timeit@functools.cache
def sum_digits(numbers):return sum(int(digit) for number in numbers for digit in str(number))numbers = tuple(random.randint(1, 1000) for _ in range(1_000_000))print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)print(timeit.timeit("sum_digits(numbers)",globals=globals(),number=1)
)

输出

0.16661812500024098
0.0018135829996026587

使用@functools.cache修饰sum_digits()等效于将sum_digits赋值给functools.cache():

# ...def sum_digits(numbers):return sum(int(digit) for number in numbers for digit in str(number))sum_digits = functools.cache(sum_digits)

请注意,我们也可以使用不同的导入方式:

from functools import cache

这样,我们就可以只使用@cache来装饰我们的函数。

其他缓存策略

Python自己的工具实现了LRU缓存策略,删除最近最少使用的条目,为新值腾出空间。
让我们来看看其他一些缓存策略:

  • 先进先出(FIFO):当该高速缓存已满时,删除添加的第一个项,为新值腾出空间。LRU和FIFO之间的区别在于,LRU将最近使用的项保存在该高速缓存中,而FIFO丢弃最旧的项而不管是否使用。
  • 后进先出(LIFO):当该高速缓存已满时,删除最近添加的项。想象一下自助餐厅里的一堆盘子。我们最近放入堆栈的盘子(最后一个)是我们将首先取出的盘子(第一个)。
  • Most-recently used(MRU):当该高速缓存中需要空间时,将丢弃最近使用的值。
  • 随机替换(RR):该策略随机丢弃一个项目,为新项目腾出空间。

这些策略还可以与有效生存期的度量相结合,有效生存期指的是该高速缓存中的一段数据被认为有效或相关的时间。想象一下缓存中的一篇新闻文章。它可能经常被访问(LRU会保留它),但一周后,新闻可能会过时。

Python中缓存时的常见挑战

我们已经了解了Python中缓存的优点。在实现缓存时,还需要记住一些挑战和缺点:

  • 缓存失效和一致性:数据可能会随着时间而变化。因此,存储在缓存中的值也可能需要更新或删除。
  • 内存管理:在缓存中存储大量数据需要内存,如果缓存无限增长,这可能会导致性能问题。
  • 复杂性:添加缓存会在创建和维护该高速缓存时给系统带来复杂性。通常,好处大于这些成本,但这种增加的复杂性可能会导致难以发现和纠正的错误。

结论

当对同一数据重复执行计算密集型操作时,我们可以使用缓存来优化性能。

Python有两个装饰器在调用函数时创建缓存:functools模块中的@lru_cache和@cache。

但是,我们需要确保该高速缓存保持最新,并正确管理内存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/17507.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

DAHL:利用由跨越 29 个类别的 8,573 个问题组成的基准数据集,评估大型语言模型在生物医学领域长篇回答的事实准确性。

2024-11-14,由首尔国立大学创建的DAHL数据集,为评估大型语言模型(LLMs)在生物医学领域长文本生成中的幻觉问题提供了一个重要的工具,这对于提高模型的准确性和可靠性具有重要意义。 数据集地址:DAHL|生物医…

递归算法专题

题目&#xff1a; 方法一&#xff1a;不讲武德法&#xff0c;注意&#xff1a;面试不能用&#xff01;&#xff01; 代码&#xff1a; public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {//不讲伍德方法for(int x : A) C.add(x); …

验证双随机矩阵(doubly stochastic matrix) 满足C(P)=C(P^T)

验证双随机矩阵(doubly stochastic matrix) 满足C( P P P)C(P T ^T T) 双随机矩阵&#xff1a; 在数学中&#xff0c;一个双随机矩阵&#xff08;doubly stochastic matrix&#xff09;是一个满足以下条件的矩阵&#xff1a; 非负矩阵&#xff1a;矩阵中的每个元素都是非负的…

海外媒体发稿:中东地区阿拉伯邮报Arab Post新闻媒体宣发

​今天&#xff0c;我们要特别聚焦于中东地区的知名新闻媒体——阿拉伯邮报&#xff08;Arab Post&#xff09;&#xff0c;探讨其在海内外媒体宣发领域的重要性和影响力。 阿拉伯邮报作为一家备受关注的新闻媒体&#xff0c;涵盖了新闻、政治、娱乐和观点等多个领域&#xff…

Mysql-DQL语句

文章目录 DQL 语句简单查询查询表所有数据查询指定列 别名查询清除重复值查询结果参与运算 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Mysql专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月16日11点39分 DQL 语句 DQL 语句数据…

ERP软件市场展望:2025年的规模与趋势深度解析-亿发

随着数字化转型的深入&#xff0c;ERP软件市场正迎来新一轮的增长。预计到2025年&#xff0c;全球ERP软件市场规模将持续扩大&#xff0c;中国市场也将保持强劲的增长势头。 市场规模增长 根据市场研究报告&#xff0c;全球ERP软件市场在2019年已超过3,000亿美元&#xff0c;预…

推荐15个2024最新精选wordpress模板

以下是推荐的15个2024年最新精选WordPress模板&#xff0c;轻量级且SEO优化良好&#xff0c;适合需要高性能网站的用户。中文wordpress模板适合搭建企业官网使用。英文wordpress模板&#xff0c;适合B2C网站搭建&#xff0c;功能强大且兼容性好&#xff0c;是许多专业外贸网站的…

LLMs 损失函数篇

LLMs 损失函数篇 一、介绍一下 KL 散度 KL&#xff08;Kullback-Leibler&#xff09;散度衡量了两个概率分布之间的差异。公式为&#xff1a; D K L ( P ∥ Q ) ∑ P ( x ) log ⁡ P ( x ) Q ( x ) D_{KL}(P \| Q) \sum P(x) \log \frac{P(x)}{Q(x)} DKL​(P∥Q)∑P(x)logQ…

智慧社区管理系统提升物业服务效率与居民生活质量

内容概要 智慧社区管理系统正变得越来越重要&#xff0c;它为现代物业管理提供了全新的视角和方法。通过结合先进的技术&#xff0c;这套系统帮助物业公司优化其服务流程&#xff0c;使服务效率得到显著提升。想象一下&#xff0c;业主只需在手机上轻轻一点&#xff0c;就能完…

共享门店模式:创新零售的新篇章

​在消费升级和数字化转型的双重浪潮下&#xff0c;传统零售业正面临前所未有的挑战与机遇。其中&#xff0c;共享门店模式作为一种创新的商业模式&#xff0c;正逐渐成为实体店铺应对电商冲击、提升运营效率和市场竞争力的重要途径。本文将深入解析共享门店模式的内涵、优势、…

基于SpringBoot的旅游网站(程序+数据库+报告)

基于SpringBoot的旅游网站&#xff0c;系统包含两种角色&#xff1a;管理员、用户,系统分为前台和后台两大模块&#xff0c;主要功能如下。 【前台】&#xff1a; - 首页&#xff1a;展示旅游网站的核心内容&#xff0c;包括推荐的旅游线路、最新的旅游资讯等。 - 旅游线路&am…

shell编程--永久环境变量和字符串显位

环境变量 echo $HOME 在终端输出后会显示家目录有个root变量 我们会提出个疑问为什么平时我们在终端输入sl 或者which等等命令会输出一些内容呢&#xff0c;这是因为这些命令都有对应的环境变量。 我们查看一下环境变量 在终端输入&#xff1a; echo $PATH 我们看一下输出…

element ui 搜索框中搜索关键字标红展示

示例如图 el-select上绑定remote-method属性 <el-select v-model"checkForm.type" filterable remote reserve-keyword :remote-method"remoteMethod" :loading"loading"><el-option v-for"item in options" :key"ite…

华为Mate 70临近上市:代理IP与抢购攻略

随着科技的飞速发展&#xff0c;智能手机已经成为我们日常生活中不可或缺的一部分。而在众多智能手机品牌中&#xff0c;华为一直以其卓越的技术和创新力引领着行业的发展。近日&#xff0c;华为Mate 70系列手机的发布会正式定档在11月26日&#xff0c;这一消息引发了众多科技爱…

《Java核心技术 卷I》用户界面中首选项API

首选项API 在桌面程序中&#xff0c;通常都会存储用户首选项&#xff0c;如用户最后处理的文件、窗口的最后位置等。 利用Properties类可以很容易的加载和保存程序的配置信息&#xff0c;但有以下缺点&#xff1a; 有些操作系统没有主目录概念&#xff0c;很难为匹配文件找到…

win10海量文件拷贝的方法

文章目录 win10海量文件拷贝的方法概述笔记备注拷贝失败的情况记录杀毒软件拦截 是否要开启"发生错误继续"的选项还是不要开启"完美校验"可以勾选"错误时继续"选项"完美校验"太占用时间了备注日志是混合编码的总结END win10海量文件拷…

Linux——环境基础开发工具使用1

目录 1.软件包管理器 1.1 操作生态系统 1.2 yum具体操作 2.编辑器Vim 2.1 vim初识 2.2 vim的基本概念 2.3 vim的基本操作 2.3.1 命令模式 2.3.2 插入模式 2.3.3 底行模式 2.3.4 补充 3.编译器gcc/g 3.1 背景知识 3.1.1 预处理&#xff08;进行宏替换/去注释/…

自定义菜单栏实现点击添加按钮打开渲染进程的Dialog.vue模态框

实现思路&#xff1a;渲染进程页面初始化后就通知主进程&#xff0c;然后把event事件保存在该js文件外&#xff0c;当点击添加时因为是在其他位置&#xff0c;所以才要这样使用。然后点击添加后由主进程主动向渲染进程传递参数通知要做的操作。 代码如下&#xff1a; // 第一步…

[vulnhub] Chronos: 1

https://www.vulnhub.com/entry/chronos-1,735/ ps&#xff1a;该靶机需要在hosts文件添加chronos.local记录&#xff0c;在官方地址上没有写 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是7 &#xff0c;kali是10 nmap -sP 1…

基于SSM的餐饮管理系统的设计与实现

【Java】基于SSM的餐饮管理系统的设计与实现 点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/90001206?spm1001.2014.3001.5503 2、技术框架&#xff1a;Jdk1.8&#xff0c;SSM&#xff0c;Tomcat&#xff0c;Mysql5&#xff0c;Jsp 3、压…