Python @lru_缓存装饰器缓存未命中过多

Python @lru_缓存装饰器缓存未命中过多,python,function,caching,memoization,functools,Python,Function,Caching,Memoization,Functools,如何配置lru\u cache根据接收到的实际值为其缓存设置键,而不是如何调用函数 >>> from functools import lru_cache >>> @lru_cache ... def f(x=2): ... print("reticulating splines...") ... return x ** 2 ... >>> f() reticulating splines... 4 >

如何配置
lru\u cache
根据接收到的实际值为其缓存设置键,而不是如何调用函数

>>> from functools import lru_cache
>>> @lru_cache
... def f(x=2):
...     print("reticulating splines...")
...     return x ** 2
...
>>> f()
reticulating splines...
4
>>> f(2)
reticulating splines...
4
>>> f(x=2)
reticulating splines...
4

换句话说,只有上面的第一个调用应该是缓存未命中,另外两个应该是缓存命中。

要做到这一点,您必须经历将参数绑定到形式参数的过程。实际的实现过程是在没有公共接口的C代码中实现的,但是在中有一个(慢得多的)重新实现。这比正常使用
functools.lru\u缓存大约慢100倍:

import functools
import inspect

def mycache(f=None, /, **kwargs):
    def inner(f):
        sig = inspect.signature(f)
        f = functools.lru_cache(**kwargs)(f)
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            return f(*bound.args, **bound.kwargs)
        return wrapper
    if f:
        return inner(f)
    return inner

@mycache
def f(x):
    print("reticulating splines...")
    return x ** 2
如果该方法的性能损失太大,您可以改为使用以下技巧,这需要更多的代码复制,但运行速度要快得多,仅比正常情况下使用
lru_cache
慢2倍左右(有时更快,带有关键字参数):


这使用了更快的C级参数绑定来规范化对已记忆助手函数的调用,但需要复制函数的参数3次:一次在外部函数的签名中,一次在助手的签名中,并且在调用帮助程序时执行一次。

do state:Distinct参数模式可能被认为是具有单独缓存项的不同调用。例如,f(a=1,b=2)和f(b=2,a=1)的关键字参数顺序不同,可能有两个单独的缓存项。看起来您可以使用
f.cache_info()
方法查看实际的缓存命中/未命中。我知道,但我认为这不是一个合理的默认行为-这些调用在所有实际用途上都是相同的。我希望以某种方式包装(或替换)lru缓存,以避免同一基础调用的所有不同拼写的缓存未命中。@bnaecker:不,因为问题试图实现的行为取决于函数签名,而函数签名是
make_key
不知道的。为什么是
f()
f(x=2)
不一样吗?在这两种情况下,
args=()
kwds={'x':2}
不是吗?@mkrieger1:no。默认值不是关键字参数。效果很好。你的C怎么样?是否有兴趣将CPython PR直接添加到
lru\u缓存中的“normalize”参数组合在一起?
@functools.lru_cache
def _f(x):
    print("reticulating splines...")
    return x ** 2

def f(x=2):
    return _f(x)