Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I memoize a Python generator?

Tags:

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 stackoverflow question) but on subsequent calls it just yields an empty sequence, presumably because a generator's values can only be yielded once.

How could I modify the memoization decorator that works for Python generators? I realise I will need to store it in memory at some point but I'd like to handle this within the decorator and not modify the original function.

The current code of the memoization function is:

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 
like image 284
bryn Avatar asked Dec 30 '10 22:12

bryn


People also ask

Can you unpack a generator Python?

You can carry out the unpacking procedure for all kinds of iterables like lists, tuples, strings, iterators and generators. There are 2 ways to unpack iterables in Python. For known length iterables - Providing the exact number of variables to unpack as the number of elements in the sequence/iterable.

Can a generator be called multiple times in Python?

A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.

What can you do with a Python generator?

Python Generator functions allow you to declare a function that behaves likes an iterator, allowing programmers to make an iterator in a fast, easy, and clean way. An iterator is an object that can be iterated or looped upon. It is used to abstract a container of data to make it behave like an iterable object.


2 Answers

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__  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 
like image 155
Jasmijn Avatar answered Sep 28 '22 01:09

Jasmijn


from itertools import tee  sequence, memoized_sequence = tee (sequence, 2) 

Done.

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

like image 45
jsbueno Avatar answered Sep 28 '22 02:09

jsbueno