Skip to content Skip to sidebar Skip to footer

Can I Memoize A Python Generator?

I have a function called runquery that makes calls to a database and then yields the rows, one by one. I wrote a memoize decorator (or more accurately, I just stole one from this s

Solution 1:

I realise this is somewhat of an old question, but for those who want a full solution: here's one, based on jsbueno's suggestion:

from itertools import tee
from types import GeneratorType

Tee = tee([], 1)[0].__class__

defmemoized(f):
    cache={}
    defret(*args):
        if args notin cache:
            cache[args]=f(*args)
        ifisinstance(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

Solution 2:

from itertools import tee

sequence, memoized_sequence = tee (sequence, 2)

Done.

It is easier for generators because the standard lib has this "tee" method!

Solution 3:

Yes. There's a decorator posted here. Take note that as the poster says, you lose some of the benefit of lazy evaluation.

defmemoize(func):
    definner(arg):
        ifisinstance(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


@memoizedefgen(x):
    ifnot x:
        yield0returnfor 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

printprint"Round 2"for a in gen([2, 3, 4, 5]):
    print a

Solution 4:

Similar to the other answers, but simpler if you know that f is a generator:

defmemoized_generator(f):
    cache = {}
    @functools.wraps(f)defwrapper(*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

Post a Comment for "Can I Memoize A Python Generator?"