Python 带有默认可选参数的备忘录/缓存
我想制作一个python装饰器,用于记忆函数。例如,如果Python 带有默认可选参数的备忘录/缓存,python,caching,memoization,Python,Caching,Memoization,我想制作一个python装饰器,用于记忆函数。例如,如果 @memoization_decorator def add(a, b, negative=False): print "Computing" return (a + b) * (1 if negative is False else -1) add(1, 2) add(1, b=2) add(1, 2, negative=False) add(1, b=2, negative=False) add(a=1, b
@memoization_decorator
def add(a, b, negative=False):
print "Computing"
return (a + b) * (1 if negative is False else -1)
add(1, 2)
add(1, b=2)
add(1, 2, negative=False)
add(1, b=2, negative=False)
add(a=1, b=2, negative=False)
add(a=1, b=2)
我希望输出是
Computing
3
3
3
3
3
3
在最后6行的任何排列下,输出应相同
这相当于找到一个映射,将等价的*args、**kwargs**
集发送到备忘缓存的唯一键dict
。上述示例中,**kwargs等于
(1, 2), {}
(1,), {'b': 2}
(1, 2), {'negative': False}
(1,), {'b': 2, 'negative': False}
(), {'a': 1, 'b': 2, 'negative': False}
(), {'a': 1, 'b': 2}
对于您可以使用的备忘录 <> > >编辑:您的用例的问题是,如果它们指定参数的方式不同,则不考虑两个函数调用是相同的。为了解决这个问题,我们可以编写我们自己的decorator,它位于
lru\u cache()
之上,并将参数转换为一种规范形式:
from functools import lru_cache, wraps
import inspect
def canonicalize_args(f):
"""Wrapper for functools.lru_cache() to canonicalize default
and keyword arguments so cache hits are maximized."""
@wraps(f)
def wrapper(*args, **kwargs):
sig = inspect.getargspec(f.__wrapped__)
# build newargs by filling in defaults, args, kwargs
newargs = [None] * len(sig.args)
newargs[-len(sig.defaults):] = sig.defaults
newargs[:len(args)] = args
for name, value in kwargs.items():
newargs[sig.args.index(name)] = value
return f(*newargs)
return wrapper
@canonicalize_args
@lru_cache()
def add(a, b, negative=False):
print("Computing")
return (a + b) * (1 if negative is False else -1)
现在,对于问题中的整个调用集,add()
只调用一次。每次调用都是使用所有三个按位置指定的参数进行的。您可以使用来获取函数的规范参数列表。用一个装饰器包装它应该不会太难
In [1]: def add(a, b, negative=False):
...: print("Computing")
...: return (a + b) * (1 if negative is False else -1)
...:
...:
In [2]: inspect.getcallargs(add, 1, 2)
Out[2]: {'a': 1, 'b': 2, 'negative': False}
In [3]: inspect.getcallargs(add, 1, 2, True)
Out[3]: {'a': 1, 'b': 2, 'negative': True}
In [4]: inspect.getcallargs(add, 1, 2, negative=False)
Out[4]: {'a': 1, 'b': 2, 'negative': False}
In [5]: inspect.getcallargs(add, 1, b=2, negative=False)
Out[5]: {'a': 1, 'b': 2, 'negative': False}
In [6]: inspect.getcallargs(add, 1, b=2)
Out[6]: {'a': 1, 'b': 2, 'negative': False}
我试了你的建议,但不起作用。例如,连续执行
add(1,2)
和add(1,2,negative=False)
两次执行Computing\\3
,因此第二次调用是计算的,而不是从缓存返回的。@JonWarneke:我明白你的意思了。我已经扩展了我的答案,以包括完整的解决方案。对于除了常规参数之外还接受**kwargs
的函数,这似乎是不正确的。inspect.getargspec
从3.0版开始就被弃用:对于更新的API,使用getfullargspec()
,它通常是一个替代品,但也能正确处理函数注释和仅关键字参数。自3.5版以来已弃用:改用Signature.bind()
和Signature.bind\u partial()
。