我想为类方法创建一个“缓存”装饰器,它在内部类属性中注册避免计算多次的方法的结果(并且我不想使用一个简单的属性,在__init__
中计算,因为我不确定一直计算它)。Python装饰器处理装饰函数的默认参数
的第一个想法是创建一个装饰“缓存”与此类似:
def cache(func):
name = "_{:s}".format(func.__name__)
def wrapped(obj):
if not hasattr(obj, name) or getattr(obj, name) is None:
print "Computing..."
setattr(obj, name, func(obj))
else:
print "Already computed!"
return getattr(obj, name)
return wrapped
class Test:
@cache
def hello(self):
return 1000 ** 5
一切正常:
In [121]: t = Test()
In [122]: hasattr(t, '_hello')
Out[122]: False
In [123]: t.hello()
Computing...
Out[123]: 1000000000000000
In [124]: t.hello()
Already computed!
Out[124]: 1000000000000000
In [125]: hasattr(t, '_hello')
Out[125]: True
现在让我们说,我想要做同样的事情,但是当方法可以用参数调用时(keyworded和/或不)。 当然,现在我们将把结果存储在不同的属性中(名字是什么?),但是在一个字典中,它的键由* args和** kwargs组成。让我们与元组做到这一点:
def cache(func):
name = "_{:s}".format(func.__name__)
def wrapped(obj, *args, **kwargs):
if not hasattr(obj, name) or getattr(obj, name) is None:
setattr(obj, name, {})
o = getattr(obj, name)
a = args + tuple(kwargs.items())
if not a in o:
print "Computing..."
o[a] = func(obj, *args, **kwargs)
else:
print "Already computed!"
return o[a]
return wrapped
class Test:
@cache
def hello(self, *args, **kwargs):
return 1000 * sum(args) * sum(kwargs.values())
In [137]: t = Test()
In [138]: hasattr(t, '_hello')
Out[138]: False
In [139]: t.hello()
Computing...
Out[139]: 0
In [140]: hasattr(t, '_hello')
Out[140]: True
In [141]: t.hello(3)
Computing...
Out[141]: 0
In [142]: t.hello(p=3)
Computing...
Out[142]: 0
In [143]: t.hello(4, y=23)
Computing...
Out[143]: 92000
In [144]: t._hello
Out[144]: {(): 0, (3,): 0, (4, ('y', 23)): 92000, (('p', 3),): 0}
多亏了事实的方法items
变成了一个元组的字典,而不考虑在字典顺序上,它完美的作品,如果keyworded参数不被调用同一订单:
In [146]: t.hello(2, a=23,b=34)
Computing...
Out[146]: 114000
In [147]: t.hello(2, b=34, a=23)
Already computed!
Out[147]: 114000
这里是我的问题:如果该方法具有默认参数,那么它不工作了:
class Test:
@cache
def hello(self, a=5):
return 1000 * a
现在不活像k:
In [155]: t = Test()
In [156]: t.hello()
Computing...
Out[156]: 5000
In [157]: t.hello(a=5)
Computing...
Out[157]: 5000
In [158]: t.hello(5)
Computing...
Out[158]: 5000
In [159]: t._hello
Out[159]: {(): 5000, (5,): 5000, (('a', 5),): 5000}
结果计算3次,因为参数没有以相同的方式给出(即使它们是“相同的”参数!)。
有人知道我怎么可以捕捉给装饰器内的函数的“默认”值?
谢谢
你知道[functools.lru_cache](https://docs.python.org/3/library/functools.html#functools.lru_cache)吗? –
@Jonas Wielicki它在3.2以下的Python中不可用。他使用Python 2.x –
@VadimShkaberda对,我没有注意到它是在3.2中添加的。 –