我有一个名为runquery
的函数,它调用数据库,然后逐个生成行。我写了一个memoize装饰器(或者更确切地说,我只是从this stackoverflow question偷了一个)但是在后续调用中它只产生一个空序列,大概是因为生成器的值只能产生一次。
我怎样才能修改适用于Python生成器的memoization装饰器?我意识到我需要在某些时候将它存储在内存中,但我想在装饰器中处理它而不是修改原始函数。
记忆功能的当前代码是:
def memoized(f):
# Warning: Doesn't work if f yields values
cache={}
def ret(*args):
if args in cache:
return cache[args]
else:
answer=f(*args)
cache[args]=answer
return answer
return ret
答案 0 :(得分:20)
我意识到这有点老问题,但对于那些想要完整解决方案的人来说:这是基于jsbueno的建议:
from itertools import tee
from types import GeneratorType
Tee = tee([], 1)[0].__class__
def memoized(f):
cache={}
def ret(*args):
if args not in cache:
cache[args]=f(*args)
if isinstance(cache[args], (GeneratorType, Tee)):
# the original can't be used any more,
# so we need to change the cache as well
cache[args], r = tee(cache[args])
return r
return cache[args]
return ret
答案 1 :(得分:10)
from itertools import tee
sequence, memoized_sequence = tee (sequence, 2)
完成。
生成器更容易,因为标准的lib具有这种“tee”方法!
答案 2 :(得分:4)
是。有一个装饰师here。请注意,正如海报所说,你失去了懒惰评估的一些好处。
def memoize(func):
def inner(arg):
if isinstance(arg, list):
# Make arg immutable
arg = tuple(arg)
if arg in inner.cache:
print "Using cache for %s" % repr(arg)
for i in inner.cache[arg]:
yield i
else:
print "Building new for %s" % repr(arg)
temp = []
for i in func(arg):
temp.append(i)
yield i
inner.cache[arg] = temp
inner.cache = {}
return inner
@memoize
def gen(x):
if not x:
yield 0
return
for i in xrange(len(x)):
for a in gen(x[i + 1:]):
yield a + x[0]
print "Round 1"
for a in gen([2, 3, 4, 5]):
print a
print
print "Round 2"
for a in gen([2, 3, 4, 5]):
print a
答案 3 :(得分:0)
与其他答案类似,但是如果您知道 f
是生成器,则更简单:
def memoized_generator(f):
cache = {}
@functools.wraps(f)
def wrapper(*args, **kwargs):
k = args, frozenset(kwargs.items())
it = cache[k] if k in cache else f(*args, **kwargs)
cache[k], result = itertools.tee(it)
return result
return wrapper