-
-
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: