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
