For a project, I would like to cache the results computed by a method. Some constraints:
- I can't use Python3
- I can't use 3rd party modules
What I've come up so far is this:
def cached_method(fun):
"""A memoize decorator for class methods."""
@functools.wraps(fun)
def get(self, *args, **kwargs):
"""Return the value if cached.
Get it, cache it and return it if not.
"""
try:
self._cache[fun]
except AttributeError:
self._cache = {}
except KeyError:
pass
return self._cache.setdefault(fun, {}).setdefault((args, tuple(kwargs.items())), fun(self, *args, **kwargs))
return get
However, there's something wrong. In fact, using a minimal example, such as this:
class TestClass(object):
@cached_method
def method(self, p_arg):
print 'sentinel'
return p_arg
sut = TestClass()
sut.method(2)
sut.method(2)
You'll see the method is called twice ('sentinel' is printed twice)
Inspecting it via PDB I can't see what the problem is, given the second time _cache
is effectively containing all what's needed.
Edit
As pointed out in the comments, the issue is with setdefault
which calls the argument passed as default (the second) regardless whether the key is found or not.
To fix that problem, replace the return
line above with:
results = self.cache.setdefault(fun, {})
try:
ret = results[(args, tuple(kwargs.items()))]
except KeyError:
ret = results[(args, tuple(kwargs.items()))] = fun(self, *args, **kwargs)
return ret