Python装饰器练习 —实现一个缓存cache+可过期被清除的功能

  • Post author:
  • Post category:python




实现一个cache 缓存,实现可过期被清除的功能

简化设计,函数的形参定义不包含可变位置参数、可变关键词参数和keyword-only参数

可以不考虑缓存大小,也不用考虑缓存满了之后的换出问题

编写的函数,满足:

def add(x=4, y=5):
	time.sleep(3)
	return x + y
以下6种,可以认为是同一种调用
print(1, add(4,5))
print(2, add(4))
print(3, add(y=5))
print(4, add(x=4,y=5))
print(5, add(y=5,x=4))
print(6, add())



分析:



数据类型的选择

缓存的应用场景,是有数据需要频繁查询,且每次查询都需要大量计算或者等待时间之后才能返回结果的情况,使

用缓存来提高查询速度,用内存空间换取查询、加载的时间。


cache应该选用什么数据结构?


便于查询的,且能快速获得数据的数据结构。

每次查询的时候,只要输入一致,就应该得到同样的结果(顺序也一致,例如减法函数,参数顺序不一致,结果不

一样)

基于上面的分析,此数据结构应该是字典。

通过一个key,对应一个value。

key是参数列表组成的结构,value是函数返回值。难点在于key如何处理。


key的存储


key必须是

hashable



key能接受到位置参数和关键字参数传参。

位置参数是被收集在一个tuple中的,本身就有顺序。

关键字参数被收集在一个字典中,本身无序,这会带来一个问题,传参的顺序未必是字典中保存的顺序。如何解

决?

  1. OrderedDict行吗?可以,它可以记录顺序。
  2. 不用OrderedDict行吗?可以,用一个tuple保存排过序的字典的item的kv对。


key的异同


什么才算是相同的key呢?

import time
import functools
@functools.lru_cache()
def add(x,y):
time.sleep(3)
return x+ys

定义一个加法函数,那么传参方式就应该有以下4种:

1. add(4, 5)

2. add(4, y=5)

3. add(y=5, x=4)

4. add(x=4, y=5)

上面4种,可以有下面几种理解:

第一种:1、2、3、4都不同。

第二种:3和4相同,1、2和3都不同。

第三种:1、2、3、4全部相同。

lru_cache实现了第一种,从源码中可以看出单独的处理了位置参数和关键字参数。

但是函数定义为def add(4, y=5),使用了默认值,如何理解add(4, 5)和add(4)是否一样呢?

如果认为一样,那么lru_cache无能为力。

就需要使用inspect来自己实现算法。


key的要求


key必须是hashable。

由于key是所有实参组合而成,而且最好要作为key的,key一定要可以hash,但是如果key有不可hash类型数据,

就无法完成。

lru_cache就不可以,我们也很难实现,所以不能在实参中出现不可hash类型。


key算法设计


inspect模块获取函数签名后,取parameters,这是一个有序字典,会保存所有参数的信息。

构建一个字典params_dict,按照位置顺序从args中依次对应参数名和传入的实参,组成kv对,存入params_dict

中。

kwargs所有值update到params_dict中。

如果使用了缺省值的参数,不会出现在实参params_dict中,会出现在签名的parameters中,缺省值也在函数定义

中。


调用的方式


普通的函数调用可以,但是过于明显,最好类似lru_cache的方式,让调用者无察觉的使用缓存。

构建装饰器函数。

代码实现:

首先我们使用python自带的lru_cache模块进行编写:

#系统自带的lru_cache 模块 
import functools
import time
@functools.lru_cache()
def add(x=4,y=5):
    time.sleep(2)
    return x+y 
print(1, add(4,5))
print(2, add(4))
print(3, add(y=5))
print(4, add(x=4,y=5))
print(5, add(y=5,x=4))
print(6, add())

其运行的结果如下图所示:



版权声明:本文为qq_40498551原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。