如何使用Python内置缓存装饰器: @lru_cache,@cache 与 @cached_property

这篇具有很好参考价值的文章主要介绍了如何使用Python内置缓存装饰器: @lru_cache,@cache 与 @cached_property。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 为什么需要缓存技术?

使用缓存是优化Python程序速度的重要方法之一。如果使用得当,可以大幅减少计算资源的负载,有效加快代码运行速度

Python 的内置库 functools 模块附带了@lru_cache,@cache, @cached_property 装饰器,使用非常简便,不需要安装第3方库,不需要 redis 等数据库保存对象等,通常只需要1行代码,就可以对函数运算结果、类成员方法运算结果进行缓存。

本文将介绍这3种缓存工具的使用步骤及实例。

2. @lru_cache 缓存装饰器的使用

@lru_cache 是最常见的缓存装饰器。lru_cache 是: Last recently used cache 的简写,可以将该函数最近调用的输入参数以及结果进行缓存。如果有新的调用,先检查缓存是否有相同的输入参数,如果存在,则直接返回对应结果。如果是无参函数,第1次调用后,以后每次调用,直接返回缓存结果。

先看1个例子

from functools import lru_cache
from math import sin

@lru_cache
def sin_half(x):
    return sin(x)/2

print('first call result:',sin_half(60))
print('second call result:',sin_half(60))

上例中,第1次运行函数后,lru_cache会缓存调用参数及返回结果。第2次运行时,lru_cache都会检查输入,发现缓存中存在相同输入参数60,则从缓存中返回结果。如果函数执行的是计算量很重的任务,对于相同输入,可以明显地节省系统资源。

装饰器参数

lru_cache默认不清除缓存内容,因此缓存会无限增长,如果程序是长期运行的服务,可能存在耗尽内存的风险。 因此,必须添加1个maxsize参数:
@lru_cache(maxsize) 的参数maxsize 表示要缓存的最近调用次数.
如 @lru_cache(360) 表示,只缓存最近360次的函数调用结果。

@lru_cache(360)
def sin_half(x):
    return sin(x)/2

缓存操作

查看缓存报告: sin_half.cache_info()
强制清除缓存内容: sin_half.cache_clear()
当然也可以使用 python的 garbage collection 清除缓存。

下面使用1个示例来演示上述内容:

import functools
import gc

# 主要功能: 
# 验证  @lru_cache 装饰器,.chche_info() 和 .cache_clear() 方法的使用
#       garbage collection 的使用

@functools.lru_cache(maxsize = 300) # Max number of Last recently used cache
def fib(n):
	if n < 2:
		return n
	return fib(n-1) + fib(n-2)


fib(30)
fib.cache_clear()

# Before Clearing
print(fib.cache_info())

# After Clearing
print(fib.cache_info())

@functools.lru_cache(maxsize = None)
def gfg1():
    # insert function logic here
    pass

# 再次运行函数 
gfg1()
fib(30)
# garbage collection
gc.collect()

# All objects collected
objects = [i for i in gc.get_objects() 
           if isinstance(i, functools._lru_cache_wrapper)]

print(gfg1.cache_info())

# All objects cleared
for object in objects:
    object.cache_clear()
    
print(gfg1.cache_info())

运行程度,输出为:

CacheInfo(hits=0, misses=0, maxsize=300, currsize=0)
CacheInfo(hits=0, misses=0, maxsize=300, currsize=0)
CacheInfo(hits=0, misses=1, maxsize=None, currsize=1)
CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)

3. @cache 缓存装饰器的使用

相比@lru_cache, @cache 装饰器更轻量化,速度更快,且是线程安全,不同线程可以调用同1个函数,缓存值可以共享。

import functools
import time

@functools.cache
def fib(n):
	if n < 2:
		return n
	return fib(n-1) + fib(n-2)
   
if __name__ == '__main__':
    
    start_time = time.time()
    print(fib(400))
    end_time = time.time()
    execution_time_without_cache = end_time - start_time
    print("Time taken without cache: {:.8f} seconds".format(execution_time_without_cache))
    
    start_time = time.time()
    print(fib(400))
    end_time = time.time()
    execution_time_without_cache = end_time - start_time
    print("Time taken with cache: {:.8f} seconds".format(execution_time_without_cache))    

output:

176023680645013966468226945392411250770384383304492191886725992896575345044216019675
Time taken without cache: 0.00095391 seconds
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
Time taken with cache: 0.00000000 seconds

4. @cached_property 缓存装饰器的使用

@cached_property是一个装饰器,它将类的方法转换为属性,其值仅计算一次,然后缓存为普通属性。因此,只要实例持久存在,缓存的结果就可用,我们可以将该方法用作类的属性那样来使用,如

调用:    : instance.method
取代旧方式 : instance.method()

@cached_property是 Python 中 functools 模块的一部分。它类似于 property(),但 @cached_property 带有一个额外的功能,那就是缓存。

但是它如何减少执行时间并使程序更快?请考虑以下示例:

# Without using @cached_property

# A sample class
class Sample():

	def __init__(self, lst):
	self.long_list = lst

	# a method to find the sum of the
	# given long list of integer values
	def find_sum(self):
		return (sum(self.long_list))

# obj is an instance of the class sample
# the list can be longer, this is just
# an example
obj = Sample([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(obj.find_sum())
print(obj.find_sum())
print(obj.find_sum())

输出为:

55
55
55

在这里,假设如果我们传递1个长列表,每次调用 find_sum()方法时都会计算给定列表的总和,从而花费大量时间运行,程序最终会变慢。我们想要的是,由于列表在创建实例后不会更改,因此,如果列表的总和只计算一次,而不是每次在我们调用方法并希望访问总和时计算,那就太好了。这可以通过使用@cached_property来实现,

# With using @cached_property

from functools import cached_property

# A sample class
class Sample():
	def __init__(self, lst):
	self.long_list = lst

	# a method to find the sum of the
	# given long list of integer values
	@cached_property
	def find_sum(self):
		return (sum(self.long_list))

# obj is an instance of the class sample
obj = Sample([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(obj.find_sum)
print(obj.find_sum)
print(obj.find_sum)

使用了@cached_property装饰器后,无须重新计算,总是立即返回find_sum值。
如果需要重新计算属性,则删除该属性即可,下次调用会重新计算,在本例 中添加如下语句,

del obj.find_sum
obj.long_list = range(20,30)
print(obj.find_sum)

运行代码,输出结果为:

55
55
55
245

5. 各种缓存方法的适用场景

综上所述,建议如下:
1) 如果程序规模小,或多线程编程时,可使用@cache 装饰器。
2) 如果程度规模大,并且是长时间运行,建议使用@lru_cache 装饰器,使用方法灵活,但要注意控制缓存数量,必要时手工清理。
3) 编写类代码时,如需将某项复杂运算结果像属性那样访问时,使用@cached_property装饰器。文章来源地址https://www.toymoban.com/news/detail-515076.html

到了这里,关于如何使用Python内置缓存装饰器: @lru_cache,@cache 与 @cached_property的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [力扣146. LRU 缓存 ](https://leetcode.cn/problems/lru-cache/description/)

    力扣146. LRU 缓存 使用LinkedHashmap(HashMap的子类,能够记住插入数据的顺序). LRU是Lease Recently User的缩写,意思是最近 最少使用。比如设计一个文件缓存系统,每个文件有自己的大小和访问时间,文件缓存系统有总的大小,当往这个文件系统中放入新的文件时,如果发现超出文件

    2024年02月11日
    浏览(51)
  • 什么是浏览器缓存(browser caching)?如何使用HTTP头来控制缓存?

    前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一个系统而又亲切的学习平台。在这个

    2024年02月09日
    浏览(48)
  • LinkedHashMap如何实现LRU缓存淘汰策略?

    LRU,Least Recently Used, 最近最少使用 ,也就是优先淘汰最近最少使用的元素。 简而言之,就是将LinedHashMap的accessOrder设为true即可。 demo实现如下: 测试一下: 3.1 LinkedHashMap简介 LinkedHashMap内部维护了一个双向链表,能保证元素按插入的顺序访问,也能以访问顺序访问,可以用

    2023年04月24日
    浏览(39)
  • 惬意上手Python —— 装饰器和内置函数

     Python中的装饰器是一种特殊类型的函数,它允许用户在不修改原函数代码的情况下,增加或修改函数的行为。 具体来说, 装饰器的工作原理基于Python的函数也是对象这一事实,可以被赋值给变量、作为参数传递给其他函数或者作为其他函数的返回值。装饰器通常接受一个函

    2024年01月24日
    浏览(44)
  • 系统学习Python——单元测试unittest:内置装饰器

    分类目录:《系统学习Python》总目录 相关文章: · 单元测试unittest:框架结构 · 单元测试unittest:测试固件 · 单元测试unittest:编写测试用例 · 单元测试unittest:执行测试用例 · 单元测试unittest:用例执行次序 · 单元测试unittest:内置装饰器 · 单元测试unittest:命令行执行测

    2023年04月13日
    浏览(45)
  • rust里如何快速实现一个LRU 本地缓存?

    LRU是Least Recently Used(最近最少使用)的缩写,是一种常见的缓存淘汰算法。LRU算法的基本思想是,当缓存空间已满时,优先淘汰最近最少使用的数据,以保留最常用的数据。 在计算机系统中,LRU算法常用于缓存系统、页面置换算法等场景,以提高数据访问的效率和性能。 要

    2024年02月13日
    浏览(41)
  • Python算法题集_LRU 缓存

    本文为Python算法题集之一的代码示例 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果 key 存在于缓存中,则返回的值,否则返回 -1 。 void put(int

    2024年02月21日
    浏览(40)
  • python第6课(函数,内置高阶函数,匿名函数,装饰器)笔记

    变量名的查找规则; 在访问变量时,先查找本地变量,然后是包裹此函数的外部函数的内部变量,之后是全局变量,最后是内置变量 1 在函数内使用全局变量 2使用nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量 函数名是变量,它在创建函数时绑

    2024年02月11日
    浏览(47)
  • 设计一个LRU(最近最少使用)缓存

    约束和假设 我们正在缓存什么? 我们正在缓存Web Query的结果 我们可以假设输入是有效的,还是需要对其验证? 假设输入是有效的 我们可以假设它适应内存吗? 对 编码实现

    2024年01月24日
    浏览(60)
  • codeTop01:LRU (最近最少使用) 缓存的实现

    请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: ● LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 ● int get(int key) 如果 key 存在于缓存中,则返回的值,否则返回 -1 。 ● void put(int key, int value) 如果 key 已

    2024年03月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包