from functools import partial
from itertools import count
class MemoizedGenerator(object):
"""Memoize a generator to avoid computing any term more than once.
"""
def __init__(self, gen):
# The underlying generator
self.__gen = gen
# Memoization fields
self.__cache = []
self.__iter = None
self.__empty = False
def __call__(self, *args, **kwargs):
"""Make instances of this class callable.
This method must be present, and must be a generator
function, so that class instances work the same as their
underlying generators.
"""
if not (self.__empty or self.__iter):
self.__iter = self.__gen(*args, **kwargs)
for n in count():
# First check the cache
if n < len(self.__cache):
yield self.__cache[n]
# See if another copy of the generator emptied it
# since our last iteration
elif self.__empty:
break
# If none of the above, advance the generator
# (which may empty it)
else:
try:
term = next(self.__iter)
except StopIteration:
self.__empty = True
break
else:
self.__cache.append(term)
yield term
# This creates a decorator that works if applied to a method
# (the above will only work on an ordinary generator function)
# -- requires the Delayed Decorator recipe at
# http://code.activestate.com/recipes/577993-delayed-decorator/
memoize_generator = partial(DelayedDecorator, MemoizedGenerator)
Diff to Previous Revision
--- revision 1 2011-12-20 04:25:17
+++ revision 2 2012-01-02 05:53:19
@@ -43,4 +43,8 @@
yield term
+# This creates a decorator that works if applied to a method
+# (the above will only work on an ordinary generator function)
+# -- requires the Delayed Decorator recipe at
+# http://code.activestate.com/recipes/577993-delayed-decorator/
memoize_generator = partial(DelayedDecorator, MemoizedGenerator)