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。
但是,我们需要确保该高速缓存保持最新,并正确管理内存。
相关文章:
Python缓存:两个简单的方法
缓存是一种用于提高应用程序性能的技术,它通过临时存储程序获得的结果,以便在以后需要时重用它们。 在本文中,我们将学习Python中的不同缓存技术,包括functools模块中的 lru_cache和 cache装饰器。 简单示例:Python缓…...
原生微信小程序在顶部胶囊左侧水平设置自定义导航兼容各种手机模型
无论是在什么手机机型下,自定义的导航都和右侧的胶囊水平一条线上。如图下 以上图iphone12,13PRo 以上图是没有带黑色扇帘的机型 以下是调试器看的wxml的代码展示 注意:红色阔里的是自定义导航(或者其他的logo啊,返回之…...
经验笔记:远端仓库和本地仓库之间的连接(以Gitee为例)
经验笔记:远端仓库和本地仓库之间的连接 方法一:先创建远端仓库,再克隆到本地 创建远端仓库 登录到你的Git托管平台(如Gitee、GitHub、GitLab、Bitbucket等)。点击“New Repository”或类似按钮,创建一个新…...
利用RAGflow和LM Studio建立食品法规问答系统
前言 食品企业在管理标准、法规,特别是食品原料、特殊食品法规时,难以通过速查法规得到准确的结果。随着AI技术的发展,互联网上出现很多AI知识库的解决方案。 经过一轮测试,找到问题抓手、打通业务底层逻辑、对齐行业颗粒度、沉…...
ffplay音频SDL播放处理
1、从解码数组获取到解码后的数据 static int audio_decode_frame(VideoState *is) {int data_size, resampled_data_size;av_unused double audio_clock0;int wanted_nb_samples;Frame *af;if (is->paused)return -1;//音频数组队列获取数据do { #if defined(_WIN32)while …...
自动化仪表故障排除法
自动化仪表主要是指在企业的实际生产工程当中,开展检测、控制、执行以及显示等一系列仪表的总称。合理地利用自动化仪表能够及时地掌握企业生产的动态,并获取相应的数据,从而推动生产过程的有序运行。 在自动化控制系统中,自动化…...
WPF 中 MultiConverter ——XAML中复杂传参方式
1. XAML代码 <!-- 数据库表格 --> <!-- RowHeaderWidth"0": 把默认的行表头隐藏 --> <DataGridx:Name"xDataGrid"Grid.Row"2"hc:DataGridAttach.ShowRowNumber"True"ItemsSource"{Binding WaferInfos, ModeT…...
实验室管理现代化:Spring Boot技术方案
4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式,是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示: 图4-1系统工作原理…...
aws凭证(一)凭证存储
AWS 凭证用于验证身份,并授权对 DynamoDB 等等 AWS 服务的访问。配置了aws凭证后,才可以通过编程方式或从AWS CLI连接访问AWS资源。凭证存储在哪里呢?有以下几个方法: 一、使用文件存储 1、介绍 文件存储适用于长期和多账户配置。AWS SDK 也会自动读取配置文件中的凭证。…...
jmeter常用配置元件介绍总结之断言
系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之线程组 4.jmeter常用配置元件介绍总结之函数助手 5.jmeter常用配置元件介绍总结之取样器 6.jmeter常用配置元件介绍总结之jsr223执行pytho…...
JMeter监听器与压测监控之Grafana
Grafana 是一个开源的度量分析和可视化套件,通常用于监控和观察系统和应用的性能。本文将指导你如何在 Kali Linux 上使用 Docker 来部署 Grafana 性能监控平台。 前提条件 Kali Linux:确保你已经安装了 Kali Linux。Docker:确保你的系统已…...
MySQL8 安装教程
一、从官网下载mysql-8.0.18-winx64.zip安装文件( 从 https://dev.mysql.com/downloads/file/?id484900 下载zip版本安装包 mysql-8.0.18-winx64.zip 解压到本地磁盘中,例如解压到:D盘根目录,并改名为MySQL mysql-8.0.34-winx6…...
聚焦 NLP 和生成式 AI 的创新与未来 基础前置知识点
给学生们讲解的技术内容可以根据他们的背景、兴趣和教学目标来规划。以下是一些适合不同阶段和领域的技术主题建议,尤其是与大语言模型(如 ChatGPT)相关的内容: 1. 自然语言处理(NLP)基础 适合对 NLP 了解…...
23种设计模式-访问者(Visitor)设计模式
文章目录 一.什么是访问者模式?二.访问者模式的结构三.访问者模式的应用场景四.访问者模式的优缺点五.访问者模式的C实现六.访问者模式的JAVA实现七.代码解释八.总结 类图: 访问者设计模式类图 一.什么是访问者模式? 访问者模式(…...
ssm150旅游网站的设计与实现+jsp(论文+源码)_kaic
毕 业 设 计(论 文) 题目:旅游网站设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游网站就是在这样的大…...
【SKFramework框架】一、框架介绍
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群:398291828小红书小破站 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【Unity3D框架】SKFramework框架完全教程《全…...
Arcgis地图实战三:自定义导航功能的实现
文章目录 1.最终效果预览2.计算两点之间的距离3.将点线画到地图上4.动态展示点线的变化5.动态画线6.动态画点 1.最终效果预览 2.计算两点之间的距离 let dis this.utilsTools.returnDisByCoorTrans(qdXYData, zdXYData, "4549")当距离小于我们在配置文件中预设置的…...
LLaMA-Factory 上手即用教程
LLaMA-Factory 是一个高效的大型语言模型微调工具,支持多种模型和训练方法,包括预训练、监督微调、强化学习等,同时提供量化技术和实验监控,旨在提高训练速度和模型性能。 官方开源地址:https://github.com/hiyouga/L…...
黑马点评 秒杀下单出现的问题:服务器异常---java.lang.NullPointerException: null(已解决)
前言: 在此之前找了好多资料,查了很多,都没有找到对应解决的方法,虽然知道是userid为空,但不知道要修改哪里,还是自己的debug能力不足,以后得多加练习。。。 问题如下: 点击限时抢…...
购物街项目TabBar的封装
1.TabBar介绍 在购物街项目中 不论页面如何滚动 始终存在一个TabBar固定在该项目的底部 他在该项目中 扮演者选项卡栏的角色 内部存在若干选项 而选项中 固定存在两部分(图片文本) 其中主要涉及到TabBar/TabBarItem这些和业务无关的共享组件(建议存放于components/common中)、…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...
