python 缓存函数的设计

Python 的 decorator是一个非常一颗赛艇的语法糖,提供了很多便利。我们可以用其实现很多牛逼的功能,并且对业务代码的侵占性很小。最近业务上遇到了一个缓存的问题,如果之前请求过该函数,则返回缓存的结果就行。代码如下

def add(a,b):
    return a+b

如果add(1,2)没有调用过,则调用一次,并缓存该函数的返回结果。如果add(1,2)调用过,则无需再调用add函数,直接返回相应参数的结果就可以。好处在于,如果add是一个很耗费时间的操作,并且属于纯函数的话,则通过缓存,可以大大加快请求的速度(得先命中缓存)。

在python中, 缓存函数的设计可以通过装饰器去实现。优点在于对被缓存的函数影响比较小,甚至无影响。基本无需改动被缓存函数的代码,即可实现缓存的功能。

根据这点,我们可以写一个简单的函数来验证一下我们的思路

local_cache = {}
def cache(func):
    def inner_func(*args, **kargs):
        if args in local_cache:
            result = local_cache.get(args)
        else:
            result = func(*args, **kargs)
            local_cache[args] = result
        return result

    return inner_func

测试代码

@cache
def add(a, b):
    print("func was called")
    return a+b

print(add(1+2))
print(add(1+2))
"""
output:
func was called
3
3
"""

根据结果可以看出,add函数只调用一次。结果符合预期。但是这个函数是有问题的,如果我们通过add(a=1 ,b=2)的方式去调用,则无法命中缓存,问题在于上面调用的方式属于kargs,但是我们单纯的只判断args是否在cache中。如果我们同时判断args和kargs是否在cache中,则代码又不pythonic了。

if args in local_cache or str(kargs) in local_cache:
    do something
else :
    result = func(*args,**kargs)
    local_cache[args] = local_cache[str(kargs)] = result

这种情况,我们可以自己根据args和kargs去构造一个字典,并作为缓存的key,类似于kargs。kargs的特点是,key为函数的绑定参数名,value为传入的参数。那么问题来了,怎么获取函数的参数名呢?这里推荐使用python的inspect模块,可以得到好多类型。对于函数来讲,只需要调用getfullargspec(func)``即可获取到函数的各类信息,add`函数为例,可以得到如下的信息

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})

args则是函数的参数名,现在我们可以根据此,重构我们的代码

        def inner_func(*args, **kargs):
            signature = {**dict(zip(getfullargspec(func).args, args)), **kargs}
            if str(signature) in local_cache:
                result = local_cache.get(str(signature))
            else:
                result = func(*args, **kargs)
                local_cache.update({str(signature): result})
            return result

        return inner_func

在这里我们实现了一个缓存函数的设计,大家可以根据自己的需要,增加其他功能,例如将缓存结果保存,以便下次程序重启后继续使用。

referer

作者:烤土豆啦
链接:https://www.jianshu.com/p/fa937690b4f4
來源:简书

相关推荐

发表评论

路人甲

网友评论(1)

不高兴
你与未来 6年前 (2018-09-05) 回复