温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

python中自带缓存lru_cache怎么用

发布时间:2021-08-09 13:41:30 来源:亿速云 阅读:313 作者:小新 栏目:开发技术

这篇文章给大家分享的是有关python中自带缓存lru_cache怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

1. lru_cache的使用

1.1 参数详解

以下是lru_cache方法的实现,我们看出可供我们传入的参数有2个maxsize和typed,如果不传则maxsize的默认值为128,typed的默认值为False。其中maxsize参数表示是的被装饰的方法最大可缓存结果数量, 如果是默认值128则表示被装饰方法最多可缓存128个返回结果,如果maxsize传入为None则表示可以缓存无限个结果,你可能会疑惑被装饰方法的n个结果是怎么来的,打个比方被装饰的方法为def add(a, b):当函数被lru_cache装饰时,我们调用add(1, 2)和add(3, 4)将会缓存不同的结果。如果 typed 设置为true,不同类型的函数参数将被分别缓存。例如, f(3) 和 f(3.0) 将被视为不同而分别缓存。

def lru_cache(maxsize=128, typed=False):
    if isinstance(maxsize, int):
        if maxsize < 0:
            maxsize = 0
    elif maxsize is not None:
        raise TypeError('Expected maxsize to be an integer or None')

    def decorating_function(user_function):
        wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
        return update_wrapper(wrapper, user_function)

    return decorating_function

1.2 基本用法

在我们编写接口时可能需要缓存一些变动不大的数据如配置信息,我们可能编写如下接口:

@api.route("/user/info", methods=["GET"])
@functools.lru_cache()
@login_require
def get_userinfo_list():
    userinfos = UserInfo.query.all()
    userinfo_list = [user.to_dict() for user in userinfos]
    return jsonify(userinfo_list)

我们缓存了从数据库查询的用户信息,下次再调用这个接口时将直接返回用户信息列表而不需要重新执行一遍数据库查询逻辑,可以有效较少IO次数,加快接口反应速度。

1.3 进阶用法

还是以上面的例子,如果发生用户的删除或者新增时,我们再请求用户接口时仍然返回的是缓存中的数据,这样返回的信息就和我们数据库中的数据就会存在差异,所以当发生用户新增或者删除时,我们需要清除原先的缓存,然后再请求用户接口时可以重新加载缓存。

@api.route("/user/info", methods=["POST"])
@functools.lru_cache()
@login_require
def add_user():
    user = UserInfo(name="李四")
    db.session.add(user)
    db.session.commit()
    
    # 清除get_userinfo_list中的缓存
    get_userinfo_list = current_app.view_functions["api.get_machine_list"]
    cache_info = get_userinfo_list.cache_info()
    # cache_info 具名元组,包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize 和 当前缓存大小 currsize
    # 如果缓存数量大于0则清除缓存
    if cache_info[3] > 0:
     get_userinfo_list.cache_clear()
    return jsonify("新增用户成功")

在上面这个用法中我们,如果我们把lru_cache装饰器和login_require装饰器调换位置时,上述的写法将会报错,这是因为login_require装饰器中用了functiontools.wrap模块进行装饰导致的,具原因我们在下节解释, 如果想不报错得修改成如下写法。

@api.route("/user/info", methods=["POST"])
@login_require
@functools.lru_cache()
def add_user():
    user = UserInfo(name="李四")
    db.session.add(user)
    db.session.commit()
    
    # 清除get_userinfo_list中的缓存
    get_userinfo_list = current_app.view_functions["api.get_machine_list"]
    cache_info = get_userinfo_list.__wrapped__.cache_info()
    # cache_info 具名元组,包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize 和 当前缓存大小 currsize
    # 如果缓存数量大于0则清除缓存
    if cache_info[3] > 0:
     get_userinfo_list.__wrapped__.cache_clear()
    return jsonify("新增用户成功")

2. functiontools.wrap装饰器对lru_cache的影响

在上节我们看到,因为@login_require和@functools.lru_cache()装饰器的顺序不同, 就导致了程序是否报错, 其中主要涉及到两点:

  • login_require装饰器中是否用了@functiontools.wrap()装饰器

  • @login_require和@functools.lru_cache()装饰器的执行顺序问题

当我们了解完这两点后就可以理解上述写法了。

2.1 多个装饰器装饰同一函数时的执行顺序

这里从其他地方盗了一段代码来解释一下,如下:

def decorator_a(func):
    print('Get in decorator_a')
    def inner_a(*args,**kwargs):
        print('Get in inner_a')
        res = func(*args,**kwargs)
        return res
    return inner_a

def decorator_b(func):
    print('Get in decorator_b')
    def inner_b(*args,**kwargs):
        print('Get in inner_b')
        res = func(*args,**kwargs)
        return res
    return inner_b


@decorator_b
@decorator_a
def f(x):
    print('Get in f')
    return x * 2

f(1)

输出结果如下:

'Get in decorator_a'
'Get in decorator_b'
'Get in inner_b'
'Get in inner_a'
'Get in f'

是不是很像django中的中间件的执行顺序,其实原理都差不多。

2.2 functiontools.wrap原理

引用其他博主的描述:

Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。

补充:为了访问原函数此函数会设置一个__wrapped__属性指向原函数, 这样就可以解释上面1.3节中我们的写法了。

2.3 使用wrap装饰器前后的变化

未完待续。。。。。。。。。

3. 自制简易的my_cache

3.1 lru_cache提供的功能

lru_cache缓存装饰器提供的功能有:

  • 缓存被装饰对象的结果(基础功能)

  • 获取缓存信息

  • 清除缓存内容

  • 根据参数变化缓存不同的结果

  • LRU算法当缓存数量大于设置的maxsize时清除最不常使用的缓存结果

从列出的功能可知,python自带的lru_cache缓存方法可以满足我们日常工作中大部分需求, 可是它不包含一个重要的特性就是,超时自动删除缓存结果,所以在我们自制的my_cache中我们将实现缓存的超时过期功能。

3.2 cache的核心部件

在作用域内存在一个相对全局的字典变量cache={}

在作用域内设置相对全局的变量包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize和 当前缓存大小 currsize

第二点中的缓存信息中增加缓存加入时间和缓存有效时间

3.3 my_cache的实现

待实现。。。。。。。。。。。。

4. lru_cache缓存和redis缓存的区别

比较类型lru_cacheredis
缓存类型缓存在app进程内存中缓存在redis管理的内存中
分布式只缓存在单个app进程中可做分布式缓存
数据类型hash 参数作为key,返回结果为value有5种类型的数据结构
适用场景比较小型的系统、单体应用常用的缓存解决方案
功能缓存功能但是缺少过期时间控制,但是使用上更加便捷具备缓存需要的各种要素

感谢各位的阅读!关于“python中自带缓存lru_cache怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI