我想制作一个记忆功能的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=2, negative=False)
add(a=1, b=2)
我希望输出为
Computing
3
3
3
3
3
3
并且在最后6行的任何排列下输出应该相同。
这相当于查找将等效*args, **kwargs**
组发送到记忆缓存dict
的唯一键的映射。以上示例的*args, **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}
答案 0 :(得分:4)
对于记忆,您可以使用functools.lru_cache()
。
编辑:您的用例的问题在于,如果它们指定参数的方式不同,则不会认为两个函数调用是相同的。为了解决这个问题,我们可以编写自己的装饰器,它位于lru_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()
仅针对问题中的整个调用调用一次。每个调用都是使用位置指定的所有三个参数进行的。
答案 1 :(得分:1)
您可以使用inspect.getcallargs()
获取函数的规范参数列表。将它包装在装饰器中不应该太难。
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}