Python 实现一个cache 装饰器可过期清除功能

本文探讨了如何使用Python实现一个cache装饰器,该装饰器具备过期清除功能。选择了字典作为数据结构,以参数列表组成的元组作为key。通过inspect模块获取函数签名,构建可hash的key,并讨论了key的异同和要求。文章还介绍了装饰器的用途和应用场景,如日志、监控等。
摘要由CSDN通过智能技术生成

简化设计, 函数的形参定义不包含可变位置参数, 可变关键字参数和keyword-only参数, 可以暂时不考虑缓存大小, 也不考虑存满之后的换出问题


# 先来看这段代码
import time

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 能够接受到位置参数和关键字参数

  • 位置参数收集到一个元组中, 本身就有顺序

  • 关键字参数收集到字典中, 但字典本内元素无序, 传参顺序就会受到影响 …

    • OrderDict 可以记录顺序
    • 如果不用OrderDict 呢 ? 用一个元组排过序的字典item 的k, v对

key 的异同

什么才算相同的key 呢 ?

import time
import functools

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

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

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

对于上面4种传参方式,可以有下面几种理解 :

  • 上面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 是所有实参组合而成而且最好要作为key 的, key一定的可hash, 如果key 有不可hash类型就无法完成. lru_cache就不可以, 我们也无法实现, 所以不能在实参中出现不可hash 类型

def add1(x, y):
    return y

输出结果 :

>> add1([], 5)
5

from functools import lru_cache
import time 

@lru_cache()
def add(x, y=5):
    time.sleep(3)
    return y

输出结果 :

>> add(4)
5
>> add([], 5)

Traceback (most recent call last):
  File "C:/**..**", line 1, in <module>
    add([], 5)
TypeError: unhashable type: 'list'

缓存必须使用key, 但是key 必须可hash, 所以只能使用可以hash 的实参的函数调用

key 算法设计

  • inspect 模块获取函数签名后, 取parameters, 这是一个有序字典, 会保存所有参数信息
  • 构建一个字典params_dict, 按照位置顺序从args 中依次对应参数名和传入的实参, 组成kv 对, 存入params_dict 中
  • kwargs 所有值update 到params_dict 中
  • 如果使用了缺省值参数, 不会出现在实参params_dict 中, 缺省值也在函数定义中

调用方式

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

代码模块如下 :

from functools import wraps
import inspect

def mag_cache(fn):
    local_cache ={
   }    # 对不同函数名是不同cache
    @wraps(fn)
    def wrapper(*args, **kwargs):   # 接收各种参数
        # 构建key
        ret = fn(*args, **kwargs)
        return ret
    return wrapper

@mag_cache
def add(x, y, z=6):
    return x+y+z

目标 :

def add(x, y, z=6):
    return x+y+z

add(4, 5)
add(4, 5, 6)
add(4, z=6, y=5)
add(4, y=6, z=6)
add(x=4, y=5, z=6)
add(z=6, x=4, y=5)
  • 上面几种都等价, key 都是一样的, 这样都可以缓存

代码实现

def mag_cache(fn):
    local_cache ={
   }    # 对不同函数名是不同cache
    @wraps(fn)
    def wrapper(*args, **kwargs):
        # 构建key
        sig = inspect.signature
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值