-
-
Save codysoyland/267733 to your computer and use it in GitHub Desktop.
import functools | |
class memoize(object): | |
def __init__(self, func): | |
self.func = func | |
self.cache = {} | |
def __call__(self, *args): | |
return self.cache_get(args, lambda: self.func(*args)) | |
def __get__(self, obj, objtype): | |
return self.cache_get(obj, lambda: self.__class__(functools.partial(self.func, obj))) | |
def cache_get(self, key, gen_callback): | |
try: | |
return self.cache[key] | |
except KeyError: | |
self.cache[key] = gen_callback() | |
return self.cache[key] | |
class Adder(object): | |
@memoize | |
def add(self, arg1, arg2): | |
print 'CALCULATING', arg1, '+', arg2 | |
return arg1 + arg2 | |
@memoize | |
def subtract(arg1, arg2): | |
print 'CALCULATING', arg1, '-', arg2 | |
return arg1 - arg2 | |
def main(): | |
print subtract(10, 5) | |
print subtract(10, 5) | |
adder1 = Adder() | |
adder2 = Adder() | |
print adder1.add(5, 5) | |
print adder1.add(5, 5) | |
print adder2.add(5, 5) | |
print adder2.add(5, 5) | |
if __name__ == '__main__': | |
main() |
Thanks very much for this! It's far more useful than the more well-known memoize decorator in the Python Decorator Library wiki. Dropping it into my code led to an order-of-magnitude improvement!
Thanks for the comment. I should note that the cache never gets cleaned, therefore never dereferences any object that has run any of its memoized methods. So if your application is long-running and creates lots of objects, they will never get garbage-collected. This caveat applies to all memoization decorators I know of (for example calling a memoized function with objects as arguments), but ideally a method-memoization decorator would clear the cache for any object that is garbage-collected. The only way I know to do this would be to store the cached result dictionary on each object in a unique attribute.
At first glance, it looks like this one does the right thing (doesn't store object references): http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
I'm trying to understand your memoize example. From the point of view of being able to wrap both plain functions and class methods, what exactly does it do that http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize doesn't?
(I'm still learning decorators and descriptors, so please bear with me.)
Thanks for the response. While trying to work this out I wrote my own, pieced together from various sources and my understanding of how things work. One thing I wanted to do is cache numpy arrays by computing their sha1 digest. My code is at https://gist.github.com/1222577. I'd be interested in any comments.
Thanks for this!
However, no need to do a dictionary lookup twice if there is a cache miss: